1. pytorch的安装
cmd中输入 nvidia-smi
就可以看到支持的cuda版本,比如我的是cuda11.1
然后去pytorch官网 选择相应的命令,运行安装即可
这里选择pip安装,后面好像不需要安装cuda,cudnn(windows下),我的命令是:
pip3 install torch==1.9.1+cu111 torchvision==0.10.1+cu111 torchaudio===0.9.1 -f https://download.pytorch.org/whl/torch_stable.html
安装好之后,运行下面代码查看gpu版本的pytorch是否安装成功了
import torch
print ('Support CUDA?:' , torch.cuda.is_available())
x = torch.tensor([10.0 ])
x = x.cuda()
print (x)
y = torch.randn(2 , 3 )
y = y.cuda()
print (y)
z = x + y
print (z)
from torch.backends import cudnn
print ('Support cudnn?:' , cudnn.is_available())
Support CUDA?: True
tensor([10. ], device='cuda:0' )
tensor([[ 0.8319, 0.6084, 1.1869],
[ 0.8798, 1.5180, -0.2546]] , device='cuda:0' )
tensor([[10.8319, 10.6084, 11.1869],
[10.8798, 11.5180, 9.7454]] , device='cuda:0' )
Support cudnn?: True
从上面结果可以看出,windows下只需要运行官网的那个pip命令,就可以安装gpu版本的pytorch了,显卡驱动电脑会自己下载好的,用nvidia-smi即可查看驱动版本!至于linux下,可能需要自己下载显卡驱动,然后再运行相应的pip命令即可。
2. tensor的基本操作与运算
numpy与tensor类似,两者可以共享内存,并且相互转换方便;不过最大的区别是numpy会把ndarray放在cpu中加速运算,而torch会把产生的Tensor放到Gpu中加速运算
2.1 创建Tensor
a = torch.Tensor([1 , 2 , 3 ])
b = torch.Tensor(2 , 3 )
print (a, b)
print (a.size())
print (b.shape)
tensor ([1 ., 2 ., 3 .]) tensor ([[8.4536 e-24 , 4.5914 e-41 , 8.4536 e-24 ],
[4.5914 e-41 , 0.0000 e+00 , 0.0000 e+00 ]])
torch.Size ([3 ])
torch.Size ([2 , 3 ])
注意torch.Tensor与torch.tensor的几点区别:
torch.Tensor是torch.empty和torch.tensor之间的一种混合,但是,当传入数据时,torch.Tensor使用全局默认dtype(FloatTensor),而torch.tensor是从数据中推断数据类型。
torch.tensor(1)返回一个固定值1,而torch.Tensor(1)返回一个大小为1的张量,它是随机初始化的值。
torch.eye(2 ,2 )
torch.zeros(2 , 3 )
torch.zeros_like(torch.rand(2 , 3 ))
torch.linspace(1 , 10 , 4 )
tensor([ 1., 4., 7., 10.])
tensor([[0.1784, 0.8238, 0.5557], [0.9770, 0.4440, 0.9478]])
tensor([[ 2.2847, -0.6502, 0.1585], [ 0.8759, 0.9291, 1.0564]])
w = torch.normal(0 , 0.01 , size=(2 ,3 ), requires_grad=True )
b = torch.zeros(10 , requires_grad=True )
2.2 修改tensor的形状和数据类型
2.2.1.Tensor.view()
x = torch.randn(2 , 3 )
print (x.size(), x.dim())
x = x.view((3 , 2 ))
y = x.view(-1 )
print (x, y, sep='\n' )
torch.Size([2, 3]) 2 tensor([[-0.1727, 0.4991], [ 0.5274, 1.1405], [-0.4631, 0.5797]]) tensor([-0.1727, 0.4991, 0.5274, 1.1405, -0.4631, 0.5797])
2.2.2.Tensor.reshape()
a = torch.rand(2 , 3 )
b = a.reshape(-1 )
c = a.reshape(-1 , 6 )
print (a, b, c, sep='\n' )
print (b.shape, c.shape, sep='\n' )
tensor([[0.7053, 0.9004, 0.4589], [0.1943, 0.8618, 0.1296]]) tensor([0.7053, 0.9004, 0.4589, 0.1943, 0.8618, 0.1296]) tensor([[0.7053, 0.9004, 0.4589, 0.1943, 0.8618, 0.1296]]) torch.Size([6]) torch.Size([1, 6])
2.2.3.torch.unsequeeze()
y = torch.unsqueeze(y, 0 )
print (y, y.size(), y.numel())
tensor([[-0.1727, 0.4991, 0.5274, 1.1405, -0.4631, 0.5797]]) torch.Size([1, 6]) 6
Tensor.view()不会修改原来的tensor值,会返回一个新的修改后的值
reshape()可以由torch.reshape(),也可由torch.Tensor.reshape()调用。但view()只可由torch.Tensor.view()来调用。
如果你只想重塑张量,请使用torch.reshape。如果你还关注内存使用情况并希望确保两个张量共享相同的数据,请使用torch.view。
2.2.4.Tensor.type()修改张量的数据类型
y = torch.tensor([0 , 2 ])
y_hat = torch.tensor([[0.1 , 0.3 , 0.6 ], [0.3 , 0.2 , 0.5 ]])
y_hat = y_hat.argmax(axis=1 )
print (y_hat.dtype)
y_hat = y_hat.type (torch.float32)
print (y_hat.dtype)
torch.int64 torch.float32
2.3 索引操作
torch.manual_seed(100 )
x = torch.randn(2 ,3 )
print (x[0 , :])
print (x[:, -1 ])
mask = x > 0
x_ = torch.masked_select(x, mask)
print (x_)
tensor ([ 0.3607 , -0.2859 , -0.3938 ])
tensor ([-0.3938 , -2.3134 ])
tensor ([0.3607 , 0.2429 ])
2.4 广播机制
import numpy as np
A = np.arange(0 , 40 , 10 ).reshape(4 , 1 )
B = np.arange(0 , 3 )
A1 = torch.from_numpy(A)
B1 = torch.from_numpy(B)
print (A1, B1, sep = '\n' )
C = A1 + B1
print (C)
tensor([[ 0],
[10],
[20],
[30]] , dtype=torch.int32)
tensor([0 , 1 , 2 ], dtype=torch.int32)
tensor([[ 0, 1, 2],
[10, 11, 12],
[20, 21, 22],
[30, 31, 32]] , dtype=torch.int32)
B2 = B1.unsqueeze(0 )
B3 = B2.expand(4 , 3 )
A2 = A1.expand(4 , 3 )
print (A2, B3, sep='\n' )
tensor([[ 0, 0, 0],
[10, 10, 10],
[20, 20, 20],
[30, 30, 30]] , dtype=torch.int32)
tensor([[0, 1, 2],
[0, 1, 2],
[0, 1, 2],
[0, 1, 2]] , dtype=torch.int32)
2.5 Tensor.sum()进行求和归并
t = torch.linspace(0 , 10 , 6 )
t = t.view(2 , 3 )
t1 = t.sum (dim=0 )
t2 = t.sum (dim=0 , keepdim=True )
print (t1, t2)
tensor([ 6. , 10. , 14. ]) tensor([[ 6., 10., 14.]] )
归并操作一般涉及一个dim参数,指定沿哪个维进行归并。另一个参数是keepdim,说明输出结果中是否保留维度1,缺省情况是False,即不保留
2.6.Tensor.max()和Tensor.argmax()求最大值及最大值索引
y_hat = torch.tensor([[0.1 , 0.3 , 0.6 ], [0.3 , 0.2 , 0.5 ]])
y_sum = y_hat.max (dim=1 )
y_hat = y_hat.argmax(axis=1 )
print (y_hat, y_sum, sep='\n' )
tensor([2, 2]) torch.return_types.max( values=tensor([0.6000, 0.5000]), indices=tensor([2, 2]))
3. torch的自动求导机制-Autograd
创建叶子节点(Leaf Node)的Tensor,使用requires_grad参数指定是否记录对其的操作,以便之后利用backward()方法进行梯度求解。requires_grad参数的缺省值为False,如果要对其求导需设置为True,然后与之有依赖关系的节点会自动变为True。
可利用requires_grad_()方法修改Tensor的requires_grad属性。可以调用.detach()或with torch.no_grad():,将不再计算张量的梯度,跟踪张量的历史记录。这点在评估模型、测试模型阶段中常常用到
通过运算创建的Tensor(即非叶子节点),会自动被赋予grad_fn属性。该属性表示梯度函数。叶子节点的grad_fn为None
最后得到的Tensor执行backward()函数,此时自动计算各变量的梯度,并将累加结果保存到grad属性中。计算完成后,非叶子节点的梯度自动释放。
反向传播的中间缓存会被清空,如果需要进行多次反向传播,需要指定backward中的参数retain_graph=True。多次反向传播时,梯度是累加的
非叶子节点的梯度backward调用后即被清空
可以通过用torch.no_grad()包裹代码块的形式来阻止autograd去跟踪那些标记为.requesgrad=True的张量的历史记录。这步在测试阶段经常使用
backward()函数接收参数,该参数应和调用backward()函数的Tensor的维度相同,或者是可broadcast的维度。如果求导的Tensor为标量(即一个数字),则backward中的参数可省略。
在整个过程中,PyTorch采用计算图的形式进行组织,该计算图为动态图,且在每次前向传播时,将重新构建。其他深度学习架构,如TensorFlow、Keras一般为静态图。接下来我们介绍计算图,用图的形式来描述就更直观了,该计算图为有向无环图(DAG)
3.1 标量正向传播、反向传播过程
如图所示,圆形表示变量,矩表示算子。如表达式:z=wx+b,可写成两个表示式:y=wx,则z=y+b,其中x、w、b为变量,是用户创建的变量,不依赖于其他变量,故又称为叶子节点。为计算各叶子节点的梯度,需要把对应的张量参数requires_grad属性设置为True,这样就可自动跟踪其历史记录。y、z是计算得到的变量,非叶子节点,z为根节点。mul和add是算子(或操作或函数)。由这些变量及算子,就构成一个完整的计算过程(或前向传播过程)
我们的目标是更新各叶子节点的梯度,根据复合函数导数的链式法则,不难算出各叶子节点的梯度。
PyTorch调用backward()方法的时候,将自动计算各节点的梯度,这是一个反向传播过程,这个过程可用下图表示。且在反向传播过程中,autograd沿着下图,从当前根节点z反向溯源,利用导数链式法则,计算所有叶子节点的梯度,其梯度值将累加到grad属性中。对非叶子节点的计算操作(或Function)记录在grad_fn属性中,叶子节点的grad_fn值为None
import warnings
warnings.filterwarnings('ignore' )
x = torch.tensor([5 ])
w = torch.randn(1 , requires_grad=True )
b = torch.randn(1 , requires_grad=True )
y = torch.mul(w, x)
z = torch.add(y, b)
print ('x, w, b, y, z 的requires_grad属性:{}, {}, {}, {}, {}' .format (x.requires_grad, w.requires_grad, b.requires_grad, y.requires_grad, z.requires_grad))
print ('x, w, b, y, z 的grad_fn属性:{}, {}, {}, {}, {}' .format (x.grad_fn, w.grad_fn, b.grad_fn, y.grad_fn, z.grad_fn))
print ('x, w, b, y, z 的grad属性:{}, {}, {}, {}, {}' .format (x.grad, w.grad, b.grad, y.grad, z.grad))
z.backward(retain_graph=True )
print ('x, w, b, y, z 的grad_fn属性:{}, {}, {}, {}, {}' .format (x.grad_fn, w.grad_fn, b.grad_fn, y.grad_fn, z.grad_fn))
print ('x, w, b, y, z 的grad属性:{}, {}, {}, {}, {}' .format (x.grad, w.grad, b.grad, y.grad, z.grad))
x, w, b, y, z 的requires_grad属性:False, True, True, True, True x, w, b, y, z 的grad_fn属性:None, None, None, <MulBackward0 object at 0x000001EA2ADDB3A0>, <AddBackward0 object at 0x000001EA2AED73D0> x, w, b, y, z 的grad属性:None, None, None, None, None x, w, b, y, z 的grad_fn属性:None, None, None, <MulBackward0 object at 0x000001EA2AED7400>, <AddBackward0 object at 0x000001EA2ABD0D90> x, w, b, y, z 的grad属性:None, tensor([5.]), tensor([1.]), None, None
由于y,z是由叶子节点w, b计算得来的,因此他们的requires_grad属性也变为了True
非叶张量的张量的.grad属性正在被访问。它的.grad属性在autograde、 .backward()期间不会被填充。如果你确实想要一个非叶张量的梯度,在非叶张量上使用.retain_grad()
print ('x, w, b, y, z 的grad_fn属性:{}, {}, {}, {}, {}' .format (x.grad_fn, w.grad_fn, b.grad_fn, y.grad_fn, z.grad_fn))
print ('x, w, b, y, z 的grad属性:{}, {}, {}, {}, {}' .format (x.grad, w.grad, b.grad, y.grad, z.grad))
x, w, b, y, z 的grad_fn属性:None, None, None, <MulBackward0 object at 0x000001EA2AD2D0A0>, <AddBackward0 object at 0x000001EA2AD2D310> x, w, b, y, z 的grad属性:None, tensor([10.]), tensor([2.]), None, None
3.2 非标量反向传播
x = torch.tensor([[2 , 3 ]], dtype=torch.float , requires_grad=True )
J = torch.zeros(2 , 2 )
y = torch.zeros(1 , 2 )
y[0 , 0 ] = x[0 , 0 ]**2 + 3 *x[0 , 1 ]
y[0 , 1 ] = x[0 , 1 ]**2 + 2 *x[0 , 0 ]
y.backward(torch.Tensor([[1 , 0 ]]), retain_graph=True )
J[0 ] = x.grad
print (J[0 ])
x.grad = torch.zeros_like(x.grad)
tensor([4., 3.])
y.backward(torch.Tensor([[0 , 1 ]]))
J[1 ] = x.grad
print (J)
tensor([[4., 3.], [2., 6.]])
4. 线性回归的numpy、pytorch、tensorflow的实现
线性回归模型:首先,给出一个数组x,然后基于表达式y=3x^2+2,加上一些噪音数据到达另一组数据y,然后,构建一个机器学习模型,学习表达式y=wx2+b的两个参数w、b。利用数组x,y的数据为训练数据,然后,构建一个机器学习模型,学习表达式y=wx2+b的两个参数w、b。利用数组x,y的数据为训练数据
4.1 numpy实现
import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt
np.random.seed(100 )
x = np.linspace(-1 , 1 , 100 ).reshape(100 , 1 )
y = 3 *np.power(x, 2 ) + 2 + 0.2 *np.random.rand(x.size).reshape(100 , 1 )
plt.scatter(x, y)
plt.show()
w1 = np.random.rand(1 , 1 )
b1 = np.random.rand(1 , 1 )
print (w1, b1)
lr = 0.01
for i in range (800 ):
y_pred = np.power(x, 2 )*w1 + b1
loss = 0.5 *(y_pred - y)**2
loss = loss.sum ()
grad_w = np.sum (np.power(x, 2 ) * (y_pred - y))
grad_b = np.sum (y_pred - y)
w1-= lr * grad_w
b1-= lr * grad_b
plt.plot(x, y_pred, 'r-' , label='predict' )
plt.scatter(x, y, color='blue' , marker='o' , label='true' )
plt.xlim(-1 , 1 )
plt.ylim(2 , 6 )
plt.legend()
plt.show()
print (w1, b1)
[[0.77828922]] [[0.7795984]]
[[2.99134284]] [[2.09741738]]
4.2 pytorch实现线性回归
torch.manual_seed(100 )
x = torch.unsqueeze(torch.linspace(-1 , 1 , 100 ), dim=1 )
y = 3 * x.pow (2 ) + 2 + 0.2 *torch.rand(x.size())
plt.scatter(x.numpy(), y.numpy())
plt.show()
torch.manual_seed(100 )
w = torch.rand(1 , 1 , requires_grad=True , dtype=torch.float )
b = torch.rand(1 , 1 , requires_grad=True , dtype=torch.float )
lr = 0.01
for i in range (800 ):
y_pred = x.pow (2 ).mm(w) + b
loss = 0.5 *(y_pred - y)**2
loss = loss.sum ()
loss.backward()
with torch.no_grad():
w -= lr * w.grad
b -= lr* b.grad
w.grad.zero_()
b.grad.zero_()
plt.plot(x.numpy(), y_pred.detach().numpy(), 'r-' , label='predict' )
plt.scatter(x.numpy(), y.numpy(), color='blue' , marker='o' , label='true' )
plt.xlim(-1 , 1 )
plt.ylim(2 , 6 )
plt.legend()
plt.show()
print (w, b)
tensor([[2.9668]] , requires_grad=True) tensor([[2.1138]] , requires_grad=True)
Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.
tensor.detach()是从计算图中脱离出来。 返回一个新的tensor,新的tensor和原来的tensor共享数据内存,但不涉及梯度计算,即requires_grad=False,这样才能调用numpy()函数,将其转为ndarray
4.3 tensorflow实现
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
INFO: tensorflow:Disabling eager execution
INFO: tensorflow:Disabling v2 tensorshape
WARNING: tensorflow:From E:\Miniconda3\lib \site-packages\tensorflow\python\compat\v2_compat.py:96 : disable_resource_variables (from tensorflow.python.ops.variable_scope) is deprecated and will be removed in a future version.
Instructions for updating:
non-resource variables are not supported in the long term
INFO: tensorflow:Disabling resource variables
INFO: tensorflow:Disabling tensor equality
INFO: tensorflow:Disabling control flow v2
np.random.seed(100 )
x = np.linspace(-1 , 1 , 100 ).reshape(100 , 1 )
y = 3 *np.power(x, 2 ) + 2 + 0.2 *np.random.rand(x.size).reshape(100 , 1 )
x1 = tf.placeholder(tf.float32, shape=(None , 1 ))
y1 = tf.placeholder(tf.float32, shape=(None , 1 ))
w = tf.Variable(tf.random_uniform([1 ], 0 , 1.0 ))
b = tf.Variable(tf.zeros([1 ]))
print (w, b)
<tf.Variable 'Variable:0 ' shape =(1,) dtype =float32_ref > <tf.Variable 'Variable_1:0 ' shape =(1,) dtype =float32_ref >
y_pred = np.power(x, 2 )*w + b
loss = tf.reduce_mean(tf.square(y-y_pred))
grad_w, grad_b = tf.gradients(loss, [w, b])
lr = 0.01
new_w = w.assign(w - lr*grad_w)
new_b = b.assign(b - lr*grad_b)
执行计算图时给new_w, new_b赋值
对于tf来说,更修参数是计算图内的内容,而对于pytorch,这部分属于计算图之外
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for step in range (2000 ):
loss_value, v_w, v_b = sess.run([loss, new_w, new_b], feed_dict={x1:x, y1:y})
if step%200 == 0 :
print ("损失值,权重,偏移量分别为:{:.4f}, {}, {}" .format (loss_value, v_w, v_b))
plt.figure()
plt.scatter(x, y)
plt.plot(x, v_b + v_w*x**2 )
损失值,权重,偏移量分别为:10.3297 , [0.10263906] , [0.06177498]
损失值,权重,偏移量分别为:0.1755 , [1.6298081] , [2.5692239]
损失值,权重,偏移量分别为:0.0919 , [2.0188992] , [2.457365]
损失值,权重,偏移量分别为:0.0492 , [2.2914894] , [2.3567092]
损失值,权重,偏移量分别为:0.0271 , [2.4876125] , [2.2840495]
损失值,权重,偏移量分别为:0.0157 , [2.6287744] , [2.231749]
损失值,权重,偏移量分别为:0.0097 , [2.7303782] , [2.194105]
损失值,权重,偏移量分别为:0.0066 , [2.8035104] , [2.1670094]
损失值,权重,偏移量分别为:0.0051 , [2.8561475] , [2.1475072]
损失值,权重,偏移量分别为:0.0042 , [2.894034] , [2.1334703]
[<matplotlib.lines.Line2D at 0x1ea2aeecaf0>]
1.torch与numpy的转换
a = np.arange(16 ).reshape(4 , 4 )
b = torch.from_numpy(a)
c = b.numpy()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人