深度学习
- 学习参考资料为:b站我是土堆的PyTorch深度学习快速入门以及李沐动手学深度学习
PyTorch学习
-
链接:https://www.bilibili.com/video/BV1hE411t7RN/
-
先安装ANACONDA,去官网下载然后安装,记住安装的地方在哪
-
在ANACONDA里面新建环境的时候,遇见了这个问题:
-
具体在csdn找到了解决办法,通过删注册表解决了这个问题:
-
按Win+R快捷键,输入regedit,打开注册表编辑器找到HKEY_CURRENT_USER/Software/Microsoft/Windows/CurrentVersion/Internet Settings分支,把它下面以 Proxy 打头的键值对(如ProxyEnable,ProxyOverride,ProxyServer等)全部删除。(但是发现会重复出现这些问题)
-
还有可能的问题是:用conda安装的时候很慢,安装不下来,这时候可以用pip安装,而且要对应python版本会特别快,比如我是python3.8安装的cuda的11.8版本,用手机热点会很快(亲测)
-
还有个问题是在新建的环境里安装jupyter会出问题,然后执行了
conda install nb_conda_kernels
conda install jupyter notebook
编辑器选择
-
对比:
- python文件的块是所有行的代码
- 优点:通用,传播方便,适用于大型项目
- 缺点:需要从头运行
- python控制台
- 优点:显示每个变量的属性
- 缺点:不利于代码阅读与修改
- jupyter
- 优点:利于代码阅读及修改
- 缺点:环境需要配置
- python文件的块是所有行的代码
-
jupyter中,ctrl+enter是执行当前块,shift+enter是执行完当前块后生成下一个块
深度学习
-
这里是跟着李沐的动手学深度学习,b站指路:https://space.bilibili.com/1567748478/channel/seriesdetail?sid=358497
-
这个版本的是在linux上安装,然后我是在windows上安装,上面pytorch学习讲到了我安装时遇见的一些问题和解决方法。
-
直接从第四集开始记录,后面的代码都是在jupyter运行
04 数据操作与数据预处理
数据操作实现
#注意:这里每个#下面或者被紧贴着的上下两个#包裹着的都是直接可以在jupyter的代码块里运行的。
#首先导入torch
import torch
#张量表示一个数值组成的数组,这个数组可能有多个维度
x = torch.arange(12)
x
#可以通过张量属性的shape来访问张良的形状和张量中元素的总数
x.shape
x.numel()
#要改变一个张量的形状而不改变元素数量和元素值,可以使用reshape函数
x = x.reshape(3,4)
x
#比如上面就会将x = torch.arange(12)这里的一维数组变为三行四列的二维数组
#使用全0,全1,其他常量或者从特定分布中随机采样的数字,下面是深度2,长度3,宽度4的三维(两个三行四列的二维数组)
torch.zeros((2,3,4))
torch.ones((2,3,4))
#通过提供包含数值的python列表(或嵌套列表)来为所需张量中的每个元素赋予确定值
torch.tensor([[2,1,4,3],[2,1,4,3],[2,1,4,3],[2,1,4,3]])
#常见的标准算数运算符(+,-,*,/和**)都可以被升级为按元素运算
x = torch.tensor([1.0,2,4,8])
y = torch.tensor([2,2,2,2])
x+y,x-y,x*y,x/y,x**y
#也可以把多个张量连接在一起
x = torch.arange(12,dtype=torch.float32).reshape((3,4))
y = torch.tensor([[2.0,1,4,3],[1,2,3,4],[1,2,3,4]])
torch.cat((x,y),dim=0),torch.cat((x,y),dim=1)
#通过逻辑运算符构建二元张量
x==y
#对张量中所有元素求和会产生一个只有一个元素的张量
x.sum()
#即使形状不同,多少可以通过调用广播机制来执行按元素操作
a = torch.arange(3).reshape((3,1))
b = torch.arange(2).reshape((1,2))
a,b
a + b
#可以用[-1]选择最后一个元素,可以用[1:3]选择第二个和第三个元素(以行)
x[-1],x[1:3]
# 除了读取外,还可以通过指定索引来将元素写入矩阵
x[1,2] = 9
x
#为多个元素赋值相同的值,我们只需索引所有元素,然后为它们赋值
x[0:2, :] = 12
x
#运行一些操作可能会导致为新结果分配内存。id()函数用于获取对象的内存地址(即对象的标识符)。在代码中,before = id(y)会获取变量y所指向的张量对象的内存地址
before = id(y)
y = y + x
id(y) == before
#执行原地操作
z = torch.zeros_like(y)
print('id(z):',id(z))
z[:] = x + y
print('id(z):',id(z))
#如果在后续计算中没有重复使用x,我们可以使用x[:] = x + y或者x += y来减少内存的开销
before = id(x)
x += y
id(x) ==before
#下面是对x += y三种的区别
# x += y:这个操作会修改原始张量 x,将 y 加到 x 上,并将结果重新赋值给 x。因此,x 的内存地址不会改变。
# x = x + y:这个操作会创建一个新的张量,将 x 和 y 相加得到的结果赋值给 x。因此,x 的内存地址会改变。
# x[:] = x + y:这个操作类似于第二个操作,但是它是在原始张量的基础上进行操作,而不是创建一个新的张量。这个操作也会修改原始张量 x,但是会保持内存地址不变。
#转换成NumPy张量
a = x.numpy()
b = torch.tensor(a)
type(a),type(b)
#将大小为1的张量转换为python标量
a = torch.tensor([3.5])
a,a.item(),float(a),int(a)
数据预处理
import os
#查看当前工作目录
print(os.getcwd())
#创建一个人工数据集,并存储在csv文件
os.makedirs(os.path.join('.','\data'),exist_ok=True)
data_file = os.path.join('.','\data','house_tiny.csv')
with open(data_file, 'w') as f:
f.write('NumRooms,Alley,Price\n')
f.write('NA,Pave,127500\n')
f.write('2,NA,106222\n')
f.write('4,NA,178100\n')
f.write('NA,NA,140000\n')
#从创建的csv文件中加载原始数据集
import pandas as pd
data = pd.read_csv(data_file)
data
#为了处理缺失的数据,典型的方法包括插值和删除,这里我们考虑插值
inputs,outputs = data.iloc[:,0:2],data.iloc[:,2]
inputs = inputs.fillna(inputs.mean(numeric_only=True))
print(inputs)
#对于inputs中的类别值或者离散值,我们将‘NAN’视为一个类别,加了.astype(int),这样当是path时为1,为NA时为0,不加是true和false
inputs = pd.get_dummies(inputs,dummy_na=True).astype(int)
print(inputs)
#现在inputs和outputs中的所有条目都是数值类型,他们可以转换为张量格式
x,y = torch.tensor(inputs.values),torch.tensor(outputs.values)
x,y
05线性代数
线性代数实现
import torch
A = torch.arange(20).reshape((5,4))
A
#转置
A.T/A.t()
#对称矩阵,A等于其转置
#就像向量是标量的推广,矩阵是向量的推广一样,我们可以构建具有更多轴的数据结构
#给定具有相同形状的任何两个张量,任何按元素二元运算的结果都将是相同形状的张量
#两个矩阵按元素乘法成为哈达玛积, A⊙B
a = 2
x = torch.arange(24).reshape((2,3,4))
a + x,(a * x).shape
#指定求和汇总张量的轴
a = torch.arange(40).reshape((2,5,4))
a
# tensor([[[ 0, 1, 2, 3],
# [ 4, 5, 6, 7],
# [ 8, 9, 10, 11],
# [12, 13, 14, 15],
# [16, 17, 18, 19]],
# [[20, 21, 22, 23],
# [24, 25, 26, 27],
# [28, 29, 30, 31],
# [32, 33, 34, 35],
# [36, 37, 38, 39]]])
a_sum_axis0 = a.sum(axis=0)
a_sum_axis0,a_sum_axis0.shape
#(tensor([[20, 22, 24, 26],
# [28, 30, 32, 34],
# [36, 38, 40, 42],
# [44, 46, 48, 50],
# [52, 54, 56, 58]]),
# torch.Size([5, 4]))
#这里的意思是,取(2,5,4)里的第一个维度求和,即0+20,1+21……
a_sum_axis1 = a.sum(axis=1)
a_sum_axis1,a_sum_axis1.shape
# (tensor([[ 40, 45, 50, 55],
# [140, 145, 150, 155]]),
# torch.Size([2, 4]))
# 即取(2,5,4)里的第二个维度求和,即0+4+8+12,1+5+9+13+17……
a_sum_axis2 = a.sum(axis=2)
a_sum_axis2,a_sum_axis2.shape
#这里同上,取第三个维度求和
#一个与求和相关的量是平均值(mean或average)
a.mean(dtype=float),a.sum()/a.numel()
a.mean(axis=0,dtype=float),a.sum(axis=0)/a.shape[0]
#计算总和或者均值时保持轴数不变
a = torch.arange(40).reshape((2,5,4))
sum_a = a.sum(axis=1,keepdims=True)
#通过广播除
# 这一行代码是将原始张量 a 中的每个元素除以对应位置上的 sum_a 中的元素。这样做的目的是对每个样本的所有元素进行归一化,使得每个样本的所有元素相加等于1
a/sum_a
#某个轴计算a元素的累积总和
a.cumsum(axis=0)
#点积是相同位置按元素乘积的和
torch.dot(x,y)
#也可以按元素乘法,然后进行求和来表示两个向量的点积
torch.sum(x*y)
#矩阵向量积Ax
torch.mv(A,x)
#矩阵相乘
torch.mm(m,n)
#范数
torch.norm(u)
#L1范数
torch.abs(u).sun()
06矩阵计算
- 将导数拓展到向量(梯度)
- 梯度指向值变化最大的方向,梯度与等高线正交
- 向量与标量得到向量,向量与向量得到矩阵
- x为向量,求完导之后,列向量变成了行向量
- 几个例子:
-
这里y为向量时,求完还是列向量
-
这里就是矩阵
-
几个例子:
07自动求导
自动求导
-
标量链式法则
-
自动求导的两种模式
- 正向传播,反向传播时间复杂度的差距就是在有没有存储中间的结果,反向传播在处理中间函数导数的时候是直接提取内存中存储的中间节点来计算的。正向传播,是从最底层又重新算了一遍,先扫描一遍数据,再做导数运算。正向传播计算每一层的导数,都需要前一层的结果作为依据,譬如说,算a要遍历x或w,算b要遍历a,算z要遍历b。反向传播是直接从内存里面取第一轮正向传播时留下的结果。相比之下,时间复杂度自然就上来了,也就是用空间换时间
自动求导的实现
#对函数y = 2x^Tx关于列向量x求导
import torch
x = torch.arange(4.0)
x
# 计算y关于x的梯度之前,需要一个地方来存储梯度
x.requires_grad_(True)
x.grad
#现在计算y
y = 2 * torch.dot(x,x)
y
#通过调用反向传播函数来自动计算y关于x每个分量的梯度
y.backward()
x.grad
#判断应该是4*x
x.grad == 4 * x
#现在计算x的另一个函数
#在默认情况下,pytorch会累积梯度,我们需要清除之前的值
x.grad.zero_()
y = x.sum()
y.backward()
x.grad
#深度学习中,我们的目的不是计算微分矩阵,而是批量中每个样本单独计算的偏导数之和
x.grad.zero_()
y = x * x
#等价于y.backward(torch.ones(len(x)))
y.sum().backward()
x.grad
#将某些计算移动到记录的计算图之外
x.grad.zero_()
y = x*x
u = y.detach #把y当成一个常数,不再为x的函数
z = u * x
z.sum().backward()
x.grad == u
#即使构建函数的计算图需要通过python控制流(条件、循环或者任意函数调用),我们仍然可以计算到变量的梯度
def f(a):
b = a * 2
while b.norm() < 1000:
b = b * 2
if b.sum() > 0:
c = b
else:
c = 100 * b
return c
a = torch.randn(size=(),requires_grad=True)
d = f(a)
d.backward()
a.grad == d/a
#这里a的梯度就是函数/a,因为对于他而言,在函数里面只进行了乘常数的操作,所以是线性的