通过两个神经元的极简模型,清晰透视 Pytorch 工作原理

2023-05-16

解剖麻雀,是分析了解复杂问题的好办法。本文通过搭建只有两个神经元的网络,从根本上剖析 Pytorch 工作原理。

先附上全部源代码,然后听我慢慢唠!

import torch
import torch.nn as nn

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(1, 2)
        self.fc2 = nn.Linear(2, 1)
    def forward(self, x):
        x = torch.sigmoid(self.fc1(x))
        x = self.fc2(x)
        return x
net = Net()
x = torch.linspace(0, 1, 10).reshape(10, 1)
y = x*x - 0.5*x + 1.5625    

import torch.optim as optim
optimizer = optim.SGD(net.parameters(), lr = 0.001)
for n in range(0, 100000):
    optimizer.zero_grad()
    loss = sum(abs(net(x) - y))
    loss.backward()
    optimizer.step()
    if n % 1000 == 0:
        print(n, loss)
print('Finished Training!')

import matplotlib.pyplot as plt
plt.plot(x, y, "k*")

z=[]
x = torch.linspace(0,1,100).reshape(100,1)
for xx in x:
    zz = net(xx)
    z.append(zz)
plt.plot(x, z, "b-")

1. 两个神经元的神经网络

我计划搭建一个极简的神经网络模型。这个模型用来拟合 [ 0 , 1 ] [0,1] [0,1] 区间的一条抛物线,如果用 sigmoid 作为激活函数,两个神经元就够了。

.至于为什么只需要两个神经元,我会另写文章来加以说明。我遇到不少程序员,对于现有网络框架的调参、训练很熟练,但是一旦要他们自己创建新网络,往往不知道从何入手。原因在于对神经网络工作原理不理解。我建议,学习深度学习理论,应该自己经常搭建小型网络模型,逐步养成遇到问题能因地制宜有针对性地搭建网络模型。不要动不动就动用什么 yolo、ssd、deepsort 等现成的框架来应付差事。
.
.深度学习领域,程序员比拼的是建模能力,而不是调参能力。

网络模型用数学公式来表示如下:
y = w 3 × s i g m o i d ( w 1 × x + b 1 ) + w 4 × s i g m o i d ( w 2 × x + b 2 ) + b 3 (1) \tag1 y= w_3\times sigmoid(w_1\times x+b_1)+w_4\times sigmoid(w_2\times x+b_2) + b_3 y=w3×sigmoid(w1×x+b1)+w4×sigmoid(w2×x+b2)+b3(1)

共计 7 个参数。模型示意图如下:
在这里插入图片描述

2. Pytorch 模型

前面的神经网络,如何用 Pytorch 来表示呢?Pytorch 用 OOP 的编程模式来实现这个模型,具体地讲,就是以 torch.nn.Model 为基类,派生一个网络模型实例类。本文代码如下:

import torch
import torch.nn as nn

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(1, 2)
        self.fc2 = nn.Linear(2, 1)
    def forward(self, x):
        x = torch.sigmoid(self.fc1(x))
        x = self.fc2(x)
        return x

这个模型实现了公式(1)中的函数,开始的时候,其参数 w 1 , w 2 , w 3 , w 4 , b 1 , b 2 , b 3 w_1,w_2,w_3,w_4,b_1,b_2,b_3 w1,w2,w3,w4,b1,b2,b3 是随机的。

接下来我们看看这个模型如何工作。在 Python 环境下执行完上述命令后,继续输入下面的命令(我用的是 IPython 环境):

net = Net()
x = torch.tensor([0.5])
y = net(x)
print(y)

----------------------------------------------
tensor([-0.4313], grad_fn=<AddBackward0>)

2.1 Python 强大的 OOP 机制简化 Pytorch 应用接口

从大的方面看,我对下述代码的运行机制很感兴趣:

class Net ...
net = Net()
y = net(x)

Net 是一个类,net 是类 Net 的实例,但 net 在后面却被当成函数使用,貌似只有 C++ 才有的机制呀!这一番神操作是如何做到的呢?下面我写一段代码展示一下:

class Mul:
    rate = 100
    def __call__(self, x):
        return x * self.rate

mul = Mul()
mul(123)
----------------------------------------------
12300

net(x) 看上去像普通函数调用,实际上调用的是类 Net 的实例 net__call__ 方法。这种表达方式,说明 Python 语言表现能力非常强大,难怪 Python 被用来开发各种系统级的软件包。

为什么要费劲用一个 class 来伪装成 function 呢?原因有两点:

  • net 在执行 __call__ 的时候,需要利用内部属性变量记录计算过程中的某些状态。
  • net 在执行 __call__ 的时候,需要利用内部属性变量定义的某些计算规则。前面的例子中,net(x) 执行过程中要参考神经网络模型中的计算过程定义。

这个技巧掩盖了 net 函数计算过程背后的复杂性,使得 Pytorch 在应用过程中更加简练。

2.2 定义参数

我们知道在 (1) 式中,算法模型定义中用到了 7 个参数。这七个参数是在类 Net 的 init 方法中定义的,代码如下:

	def __init__(self):
      	super(Net, self).__init__()
        self.fc1 = nn.Linear(1, 2)
        self.fc2 = nn.Linear(2, 1)

这段代码在类 Net 构建的时候执行。它说明 Net 模型包含了两个线性变换计算,第一个 self.fc1 是 1 个输入和 2 个输出的线性变换, 第二个 self.fc2 是 2 个输入和 1 个输出的线性变换。具体一点,定义了如下变换:

self.fc1:
{ s 1 = w 1 x + b 1 s 2 = w 2 x + b 2 (2) \tag2 \begin{cases} s_1=w_1x+b_1\\ s_2=w_2x+b_2\\ \end{cases} {s1=w1x+b1s2=w2x+b2(2)
self.fc2
y = w 3 t 1 + w 4 t 2 + b 3 (3) \tag3 y=w_3t_1+w_4t_2+b_3 y=w3t1+w4t2+b3(3)

注意,这个定义中不包括 sigmoid 函数的运算,原因是该计算不包含任何参数,因此无需在 __init__ 方法中展现。实际上:
{ t 1 = s i g m o i d ( s 1 ) t 2 = s i g m o i d ( s 2 ) (4) \tag4 \begin{cases} t_1=sigmoid(s_1)\\ t_2=sigmoid(s_2) \end{cases} {t1=sigmoid(s1)t2=sigmoid(s2)(4)
或简化为:
t = s i g m o i d ( s ) (5) \tag5 \boldsymbol t = sigmoid(\boldsymbol s) t=sigmoid(s)(5)

总而言之,需要含待确定参数的运算过程,在 __init__ 中定义。那些没有参数,或者不需要通过训练参数的运算,无需在此定义。

2.3 定义计算过程

forward 方法中,要定义完整的计算过程,以便根据输入 x 计算模型的输出 y。代码如下:

    def forward(self, x):
        x = torch.sigmoid(self.fc1(x))
        x = self.fc2(x)
        return x

我们又看到神奇的一幕,self.fc1self.fc2__init__ 代码中明明是一个变量,怎么摇身一变成函数了?这个戏法和代码 net = Net(),然而 y = net(x) 是同一个原理。函数 nn.Linear(1, 2) 返回了一个类的实例,该实例重载了 __call__ 方法。

最重要的是,x = torch.sigmoid(self.fc1(x)) 这种代码,看上去和我们编写的普通函数无异,其实它并没有实际执行计算,只是把定义的计算公式以某种数据结构保存在 Net 内部了。真正的计算是在函数 __call__ 中进行的,也就是在执行 y = net(x) 的时候进行的。__call__ 函数依据定义的公式和输入数据计算输出结果。

2.4 Pytorch 中计算过程的定义

计算过程不局限于 forward 方法内。实际上,我们需要知道从输入变量 x 到最终 loss 函数计算的完整过程。因此,类似 forward 方法中的计算过程定义,我们会不断遇到。计算过程定义方法,forward 给出了很好的示范。

下面我写一个例子,这个例子中,定义了加法运算。这个加法运算并没有计算两个数字的和,而是把加法计算公式保存成一个字符串。代码如下:

class MyData:
    data=""
    def __init__(self, x):
        self.data = x
    def __add__(self, other):
        return self.data + ' + ' + other.data
a = MyData('a')
b = MyData('b')
c = a + b
print(c)
----------------------------------------------

a + b

Pytorch 就是利用这样的机制,把我们习以为常的计算公式转换成了内部定义格式,供其他函数解释执行。

那么,前面的代码中,变量 x、y 等都是些什么类型呢?在 Pytorch 中,神经网络的输入和输出都是张量。也就是 torch.Tensor 类型。这种类型重载的数学运算符,因此我们可以用常规的计算公式把公式定义保存到网络模型里。

也正因为 Python 语言的这个特点,使得它能够担当“元语言”的角色,在抽象层面定义复杂计算过程,以便开发出高级的数学计算工具。Pytorch、Tensorflow、Keras 都充分利用了运算符重载机制,描述算法模型的计算过程。

3. Pytorch 的梯度(微分)计算机制

神经网络的训练离不开微分计算。Pytorch 既然能够提供计算过程的定义机制,也就是说 Pytorch 是知道计算过程的公式结构的。既然这样,求解复杂公式的微分也是可能的。我们先看个例子:

3.1 微分计算机制

x = torch.tensor([1.0], requires_grad = True)
print("x = ", x)
y = x*x
print("y = ", y)
y.backward()
print("x.grad = ", x.grad)
----------------------------------------------

x =  tensor([1.], requires_grad=True)
y =  tensor([1.], grad_fn=<MulBackward0>)
x.grad =  tensor([2.])

下面解释一下代码:

  • x 是一个张量,我们定义的时候要求保存梯度信息:x = tensor([1.], requires_grad=True)
  • y 等于 x 的平方。当然,y = x * x 应该是借助运算符重载机制,把这个计算公式转化成内部的数据结构保存起来了。因此我们可以断定,变量 y 不仅保存了 1*1 的值,而且也保存的计算公式本身。从后面的打印结果可以证实这一结论:tensor([1.], grad_fn=<MulBackward0>)
  • y.backward() 会对其携带的公式计算微分。这个过程很神奇,无论计算过程有多少步骤,backward 都能够回溯到 y 所依赖的变量 x。我们看一下 x 的微分:x.grad = tensor([2.])。对照一下下面的数学公式,结果是正确的.

y = x 2 (6) \tag6 y=x^2 y=x2(6)

d y d x = 2 x (7) \tag7 \frac{dy}{dx}=2x dxdy=2x(7)
当 x = 1 时,梯度值 d y / d x = 2 dy/dx = 2 dy/dx=2

3.2 Pytorch 中梯度(微分)累加机制

上面的例子修改一下:

x = torch.tensor([1.0], requires_grad = True)
print("x = ", x)
y = x*x
print("y = ", y)
y.backward()
print("x.grad = ", x.grad)
y = x*x
print("y = ", y)
y.backward()
print("x.grad = ", x.grad)
x.grad = torch.tensor([0.])
print("x.grad = ", x.grad)
----------------------------------------------

x =  tensor([1.], requires_grad=True)
y =  tensor([1.], grad_fn=<MulBackward0>)
x.grad =  tensor([2.])
y =  tensor([1.], grad_fn=<MulBackward0>)
x.grad =  tensor([4.])
x.grad =  tensor([0.])

注意,x.grad 的值是累加的。这一特点很重要,因为当用大量训练样本训练时,我们需要累计每一个样本的梯度。

4. Pytorch 官网提供的梯度(微分)计算的例子

由于微分计算非常重要,这里再展示一个更复杂的微分计算的例子,来自 https://www.pytorch123.com。

import torch
x = torch.ones(2, 2, requires_grad=True)
y = x + 2
z = y * y * 3
out = z.mean()
out.backward()
print(x.grad)
----------------------------------------------

tensor([[4.5000, 4.5000],
        [4.5000, 4.5000]])

这个展示了多维输入和多维输出网络的微分计算,微分公式不再是一个简单的代数式,而是一个矩阵,该矩阵就是多元函数微积分里著名的雅可比矩阵。具体细节不多讲了,反正记住这个矩阵是训练模型的时候用于反向传播算法的。参见下面的解释。
在这里插入图片描述

5. 生成训练数据

我们让神经网络模型在 [ 0 , 1 ] [0,1] [0,1] 区间内逼近下面的函数:
y = x 2 − 0.5 x + 1.5625 (8) \tag8 y=x^2-0.5x+1.5625 y=x20.5x+1.5625(8)

基于该函数解析式,用下面的代码生成10组训练数据:

x = torch.linspace(0, 1, 10).reshape(10, 1)
y = x*x - 0.5*x + 1.5625   

其中,x是输入数据,y是训练标签。

6. 构建损失函数

损失函数结果是网络 net 针对输入 xi 预测的结果 net(xi) 与标签 yi 误差的绝对值的和。数学公式如下,其中 n n n 是训练样本数量:
l o s s = ∑ i = 1 n ∣ n e t ( x i ) − y i ∣ loss = \sum_{i=1}^{n}|net(x_i)-y_i| loss=i=1nnet(xi)yi

由于 Pytorch 中的函数和运算都是针对张量设计的,因此函数定义也很简单:

	loss = sum(abs(net(x) - y))

7. 选择梯度下降的策略

为了更好更快地训练模型,有多种梯度下降策略可以选择。由于不同的策略导致不同的训练代码,为简化这部分工作,Pytorch 提供了优化框架。训练代码如下:

import torch.optim as optim
optimizer = optim.SGD(net.parameters(), lr = 0.001)
for n in range(0, 100000):
    optimizer.zero_grad()
    loss = sum(abs(net(x) - y))
    loss.backward()
    optimizer.step()
    if n % 1000 == 0:
        print(n, loss)
print('Finished Training!')

首先我们选择 optim.SGD (随机梯度下降)作为优化策略,学习率 lr = 0.001。学习率需要具体实验才能确定。如果训练过程中,loss 的值不能稳步下降,而是不断出现徘徊,说明 lr 的值太大了。如果 loss 下降速度很慢,说明 lr 的值太小了。

本例中,我计划迭代 100000 次。

在循环体内,首先执行梯度清零。我们知道,net 的梯度值是不断累加的。在每个迭代开始时,需要清零梯度,以便计算当前模型针对训练样本的总体梯度值。

8. 显示结果

用 matplotlib 的工具先试一下训练效果:

import matplotlib.pyplot as plt
plt.plot(x, y, "k*")

z=[]
x = torch.linspace(0,1,100).reshape(100,1)
for xx in x:
    zz = net(xx)
    z.append(zz)
plt.plot(x, z, "b-")

在这里插入图片描述
相当不错哦!

有了本文的基础,相信进一步学习 Pytorch 应该非常轻松了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

通过两个神经元的极简模型,清晰透视 Pytorch 工作原理 的相关文章

  • Hexo分类及标签显示

    Hexo根目录配置 config yml category map Blogs categories Blogs Tech categories Tech Tools categories Tools Other categories Ot
  • IDEA查看历史记录

    方法一 文件内 Ctrl 43 右键 Local History Show History xff0c 显示当前文件的本地修改历史 方法二 一 xff1a 在文件内 xff0c 按 Ctrl 43 Shift 43 A 弹出全部搜索对话框
  • SpringBoot-JPA整合ShardingShpere自定义分布式主键

    分布式主键简介 在分布式环境下 xff0c 由于分库分表导致数据水平拆分后无法使用单表自增主键 xff0c 因此我们需要一种全局唯一id生成策略作为分布式主键 当前有如下解决方案 UUID xff08 Universally Unique
  • Gitlab的安装与配置

    安装开始时 xff0c 需确认服务器最小配置是2核4G xff0c 因为gitlab软件比较大 1 配置yum源 xff1a vim etc yum repos d gitlab repo gitlab name 61 gitlab ce
  • Error creating bean with name ‘org.springframework.aop.aspectj.AspectJPointcutAdvisor#0

    问题 xff1a nested exception is org springframework beans factory BeanCreationException Error creating bean with name 39 or
  • Vue前端项目开发页面(二)

    前端界面开发 开发工具版本 64 vue cli 4 5 13 新建Login vue登陆页 1 在 vue exemples 项目 xff0c 选中components目录右键 New Vue Component xff0c 名称为 Lo
  • SpringBoot整合WebSocket

    概述 HTTP 协议是一种无状态的 无连接的 单向的应用层协议 它采用了请求 响应模型 通信请求只能由客户端发起 xff0c 服务端对请求做出应答处理 WebSocket和HTTP一样 xff0c 都是一种网络通信协议 比起HTTP只能由客
  • SpringBoot整合MybatisPlus使用IPage实现分页

    概述 MybatisPlus 提供了分页的功能 IPage内部原理是基于拦截器 xff0c 但是这个拦截的是方法以及方法中的参数 xff0c 这个也会判断是否是查询操作 如果是查询操作 xff0c 才会进入分页的处理逻辑 进入分页逻辑处理后
  • SpringBoot统一异常处理

    概述 SpringBoot 提供了 64 ControllerAdvice 64 RestControllerAdvice 注解可以实现统一异常处理 xff0c 只需要在定义异常类加上以上注解即可 自定义异常处理 定义统一异常处理 span
  • 萌新学习算法——并查集基础

    并查集 在算法设计中 xff0c 将一个集合和另外一个集合合并时 xff0c 就会用到并查集 假如不用并查集 xff0c 你可能会用到集合和列表来实现 xff0c 这样会使代码看起来很复杂 xff0c 而且执行效率不高 xff0c 下面用洛
  • linux中断及其底半部-s5p6818开发平台

    中断分为两个部分 xff1a 中断顶部 xff08 top half xff09 和中断底半部 xff08 bootom half xff09 一 中断顶部 xff08 top half xff09 中断上半部需要处理一下三种情况 xff1
  • Windows如何查看.db数据库文件

    从android应用导出的 db文件 xff0c 想在Windows电脑端看 xff0c 可以用SQLite Expert Professional这个软件查看 xff0c 网上说用FireFox的插件sqlite manager xff0
  • Ubuntu工具-01 UEX

    UltraEdit是Windows旗下一款流行的老牌文本 HEX编辑器 xff08 非开源 xff09 UltraEdit正被移植到Linux平台 该移植名为UEX xff0c 意即UltraEdit for Linux UltraEdit
  • Ubuntu工具-2 OBS Studio

    文章目录 1 下载并安装1 1 Flathub安装1 2 Snap安装1 3 PPA源方式安装1 3 1 检查OpenGL版本 xff0c 其版本必须高于 96 3 3 96 1 3 2 安装虚拟摄像机驱动1 3 3 安装ffmpeg库1
  • Docker build创建指定容器镜像

    Docker build xff1a Build an image from a Dockerfile 按照Dockerfile文件所定义内容创建临时性容器 xff0c 把Docker中所定义的每行命令在临时容器中执行 xff0c 然后生成
  • Ubuntu工具-03 VLC

    文章目录 1 安装VLC Media Player的方法1 1 apt安装1 2 snap安装 xff08 未测试 xff09 2 启动VLC Media Player并设置为默认媒体播放器 VLC Media Player xff08 V
  • MySQL笔记-07 常用函数

    文章目录 1 数学函数1 1 ABS1 2 CEIL和CEILIN1 3 FLOOR1 4 MOD1 5 ROUND1 6 TRUNCATE 2 字符串函数2 1 CONCAT2 2 CONCAT WS2 3 INSERT2 4 LOWER
  • SpringMVC-01 Web基础介绍

    文章目录 1 CGI1 1 CGI原理1 2 输入 出1 3 环境变量1 3 1 与请求相关的环境变量1 3 2 与服务器相关的环境变量1 3 3 与客户端相关的环境变量1 3 4 详细说明1 3 4 1 REQUEST METHOD 1
  • SpringMVC-02 MVC模式介绍

    文章目录 1 Java Web开发模型2 JSP 43 JavaBean开发模型 xff08 model1 xff09 3 MVC开发模式 xff08 model2 xff09 3 1 MVC模式基础3 1 1 模型 视图 控制器各部分的作
  • 系统架构师-科目1考点

随机推荐

  • 系统架构师-科目2考点

  • 系统架构师-科目3考点

  • Hive笔记-01 架构概述

    文章目录 1 概述2 Metadata Metastore的作用3 Metastore三种配置方式3 1 Hive配置参数说明3 1 1 基本配置参数3 1 2 其他配置参数 3 2 内嵌模式 xff08 Embedded xff09 3
  • Hadoop笔记-01概述

    文章目录 1 什么是大数据 xff1f 1 1 大数据计算模式及代表产品1 2 云计算与物联网1 2 1 云计算1 2 1 1 虚拟化1 2 1 2 分布式存储1 2 1 3 分布式计算1 2 1 4 多租户 1 3 物联网1 3 1 识别
  • Hadoop笔记-02 安装

    文章目录 1 VBOX安装CentOS71 1 安装VBOX软件1 2 下载CentOS7镜像文件1 3 初始化VBOX虚拟盘1 4 CentOS7网络配置1 5 CentOS7 yum源配置1 6 CentOS7 一般配置1 6 1关闭防
  • ffmpeg播放器实现详解 - 视频同步控制

    1 时间戳 时间戳的概念贯穿音视频开发始终 xff0c 重要性不言而喻 时间戳告诉我们在什么时候 xff0c 用多快的速度去播哪一帧 xff0c 其中 xff0c DTS decoding timestamp 告诉我们何时解码 xff0c
  • Ubuntu22.04 安装深度微信报错 依赖: libsasl2-2 (>= 2.1.27.1)

    现象 xff1a span class token punctuation span base span class token punctuation span pang 64 pang HP span class token funct
  • Ubuntu22.04更新后 点击深度微信无反应

    系统版本 xff1a Ubuntu 22 04 jammy内核 xff1a x86 64 Linux 5 15 0 53 genericdeepin wine6 stable 版本 xff1a 6 0 0 41 1 深度微信图标点击后 xf
  • MySQL笔记-08 索引

    文章目录 1 索引概述1 1 MySQL索引分类1 1 1 普通索引1 1 2 唯一性索引1 1 3 全文索引1 1 4 单列索引1 1 5 多列索引1 1 6 空间索引 2 创建索引2 1 在建立数据表时创建索引2 1 1 普通索引创建2
  • MySQL笔记-09 视图

    文章目录 1 视图概念1 1 概念1 2 作用 2 创建视图2 1 查看创建视图的权限2 2 创建视图的步骤2 3 创建视图的注意事项 3 视图操作3 1 查看视图3 1 1 DESCRIBE语句3 1 2 SHOW TABLE STATU
  • MySQL笔记-10 数据完整性约束

    文章目录 1 定义完整性约束1 1 实体完整性1 1 1 主键约束1 1 2 候选键约束 1 2 参照完整性1 3 用户定义完整性1 3 1 非空约束1 3 2 CHECK约束1 3 2 1 对列实施CHECK约束1 3 2 2 对表实施C
  • Linux命令行笔记-00 综述

    文章目录 1 Linux命令行简介1 1 Linux命令行的分类1 1 1 根据系统中作用来分类1 1 2 根据对象来分类 2 Linux命令行解释器2 1 命令行解释器shell2 1 1 核心程序2 1 2 公用程序shell2 1 3
  • Linux命令行笔记-01 文件管理-文件的建立、移动和删除

    文章目录 1 文件的建立 移动和删除1 1 96 cat 96 建立文件1 1 1 语法格式与参数1 1 2 示例 1 2 96 touch 96 建立文件1 2 1 语法格式与参数1 2 2 示例1 2 3 注意 1 3 96 ln 96
  • CMake学习-01 综述

    文章目录 1 CMake1 1 CMake生成makefile并编译的流程 2 CMakeLists txt2 1 Demo讲解2 2 常用命令2 2 1 指定CMAKE的最低版本2 2 2 设置项目名称2 2 3 设置变量2 2 4 设置
  • Rust:官方迭代器大全

    一 for 和迭代器 先看一段代码 xff1a span class token keyword fn span span class token function definition function main span span cl
  • Rust: 函数的重载——我做的的一组小实验

    编程的时候 xff0c 我发现有不少函数能够根据左值类型自动调用重载函数 但是 xff0c 我知道 Rust 的函数是不支持重载的 所以我打算尝试一下这一 重载 现象是如何实现的 一 Rust 不支持函数重载 写一段代码 xff1a spa
  • php产生大量session文件导致报错无法创建修改文件:no space left on device

    阿里云SLB健康检测后端服务器组产生百万级别的php的0k大小session文件 今天早上在登录公司一台阿里云的服务器上vim修改配置文件以及touch文件时报错 xff1a no space left on device df h 查看了
  • Rust: Native Windows GUI下载、安装、演示入门

    上 github 下载 xff0c 网址为 https github com gabdube native windows gui 上面有安装说明 按说明方法 xff0c 老是提示权限不够 配置了 ssh 公钥证书 xff0c 仍然不行 请
  • Rust: Native Windows GUI 入门第一课,程序结构剖析

    基于派生宏的代码实例 Cargo toml 文件 span class token punctuation span package span class token punctuation span name span class tok
  • 通过两个神经元的极简模型,清晰透视 Pytorch 工作原理

    解剖麻雀 xff0c 是分析了解复杂问题的好办法 本文通过搭建只有两个神经元的网络 xff0c 从根本上剖析 Pytorch 工作原理 先附上全部源代码 xff0c 然后听我慢慢唠 xff01 span class token keywor