一、机器学习组件:
1、我们可以学习的数据(data)。
样本-特征-标签
图像的特征由像素值表示,每个空间位置的红、绿、蓝通道的强度。
固定长度的特征向量容易大量训练,所以需要裁剪成标准尺寸,要防止丢失信息。
数据要海量+正确,也不要不均衡。
2、如何转换数据的模型(model)。
3、一个目标函数(objective function),用来量化模型的有效性。
定义模型的好坏,并希望优化到最低点,学习模型参数的最佳值。又损失函数loss function。 训练数据集用于拟合模型参数,测试数据集用于评估拟合的模型。
常见,回归-预测数值:平方误差;分类:最小化错误率,交叉熵。
4、调整模型参数以优化目标函数的算法(algorithm)。
梯度下降(gradient descent)。在每个步骤中,梯度下降法都会检查每个参数,看看如果你仅对该参数进行少量变动,训练集损失会朝哪个方向移动。 然后,它在可以减少损失的方向上优化参数。
二、数据操作
1、创建(形状、元素类型、元素值)
import torch
import numpy as np # numpy是数值计算的库
x = torch.arange(12) # 0-11
x = torch.tensor([[5.5, 3],[2,3]]) # 直接创建
x = torch.empty(5, 3) # 创建未初始化Tensor
x = torch.randn(5, 3) # 创建随机初始化Tensor
x = torch.zeros(5, 3, dtype=torch.long) # 整数型
a = np.array([[1,2,3],[3,4,5]]) # 创建数组
x.numel() # 求元素个数
x.shape
x.size()
x.dim() # 求维度
2、操作
访问元素:
x[1,2]
x[1,:]
x[1:3,:] # 第2、3行
x[::3,::2] # 每隔3行、2列挑出来
运算*:
x**2 # 求幂运算
torch.cat((x,y),dim) # 将xy堆叠
x.view() # 改变形状,共用内存
x.reshape() # 开辟新内存
x=np.array([])
np.linalg.inv(x) # 矩阵求逆
3、广播机制broadcasting mechanism
形状不同依然按元素操作。首先,通过适当复制元素来扩展一个或两个数组, 以便在转换之后,两个张量具有相同的形状。 其次,对生成的数组执行按元素操作。
4、内存
Y = Y + X 给Y分配新的内存。
Y+= X 不占用新内存,减少内存开销。
可用 id(y) 检查内存。
5、对象转换
x.item() # Tensor转number,只能有一个元素
b = a.numpy() # tensor --> numpy 共享
b = torch.from_numpy(a) # numpy --> tensor 共享
c = torch.tensor(a) # numpy --> tensor 不共享
X, y = torch.tensor(inputs.values), torch.tensor(outputs.values)
Tensor可以放到GPU计算。
6、用方法to()可以将Tensor在CPU和GPU(需要硬件支持)之间相互移动。
还可以更改数据类型。
z.to("cpu", torch.double)
7、布尔型 and or not !=
8、按照某种格式输出
‘%s %s %d’%(12.4,15) # 小数、整数
二、数据预处理
1、基本文件操作
创建新文件夹
import os
path = r"./result"
path = os.path.join(path,'abc') # 拼接
if not os.path.exists(path):
os.makedirs(path)
阅读文件内容
import pandas as pd
data = pd.read_csv(data_file)
修改文件内容
with open(data_file, 'w') as f:
f.write('NumRooms,Alley,Price\n') # 列名
f.write('NA,Pave,127500\n') # 每行表示一个数据样本
f.write('2,NA,106000\n')
2、处理缺失值NaN
数值类:1插值法:替代值;2删除法:忽略。
类别值或离散值:将列分类,值为0或1。eg.“巷子类型”(“Alley”)列只接受两种类型的类别值“Pave”和“NaN”, get_dummies自动将此列转换为两列“Alley_Pave”和“Alley_nan”。 巷子类型为“Pave”的行会将“Alley_Pave”的值设置为1,“Alley_nan”的值设置为0。 缺少巷子类型的行会将“Alley_Pave”和“Alley_nan”分别设置为0和1。
inputs = inputs.fillna(inputs.mean()) # fillna空白值填充
inputs = pd.get_dummies(inputs, dummy_na=True) # 对类别型特征做 One-Hot 编码
3、其他
data.isnull().sum(axis = 0) # 计数空值数
a.idxmax() # 最大值所在索引值
del 或者 drop() # 删除
三、线性代数
-
张量是描述具有任意数量轴的 n 维数组的通用方法。
-
A*B 是按元素相乘的,Hadmard积
-
sum或求mean 会沿0轴或1轴或沿01降低张量的维度
A.sum(axis=0, keepdims = True) 保持轴数不变,通过广播将 A/sum_A,保持原来形状不变。
A.cumsum(axis=0) 前n行或列累加 -
点积 torch.dot(x,y),x的T乘y。向量*权重 (x w),若w元素非负且和为1,则相当于加权平均。
-
矩阵-向量积 torch.mv(A,x)
-
矩阵乘法 torch.mm(A,B)
-
范数norm 将向量映射到标量的函数f,满足:如果我们按常数因子 α 缩放向量的所有元素, 其范数也会按相同常数因子的绝对值缩放;f(x+y)≤f(x)+f(y);f(x)≥0
L2范数:torch.norm();L1范数:torch.abs(u).sum()
矩阵的Frobenius范数同向量的 L2 范数。
四、自动求梯度-优化
导数形状:
x列向量,y标量,y’行向量
x标量,y列向量,y’列向量
x向量,y向量,y’矩阵
一个多元函数对其所有变量的偏导数,以得到该函数的梯度(gradient)向量。
用pytorch提供的autograd包,根据输入和前向传播过程自动构建计算图并进行反向传播。
正向传播:需要每次计算传播一次
反向传播:占用内存大,需要记住中间变量。
根据我们设计的模型,系统会构建一个计算图(computational graph), 来跟踪计算是哪些数据通过哪些操作组合起来产生输出。自动微分使系统能够随后反向传播梯度。这里,反向传播(backpropagate)意味着跟踪整个计算图,填充关于每个参数的偏导数。
y.backward()调用反向传播函数来自动计算y关于x每个分量的梯度,完成所有梯度计算。
此Tensor的梯度将累积到.grad属性中,x.grad打印梯度。
在y.backward()时,如果y是标量,则不需要为backward()传入任何参数,所以常用y.sum();否则,需要传入一个与y同形的Tensor。
创建一个tensor并设置requires_grad=True,它将开始追踪(track)在其上的所有操作,就可以利用链式法则梯度传播了。
import torch
x = torch.ones(2, 2, requires_grad=True)
x.requires_grad_(True) # False
y = x + 2
out = y.mean() # out 是个标量
out.backward()
print(x.grad)
grad在反向传播过程中是累加的,所以一般需要先清零,后缀_一般表示重写
out3 = y.sum()
x.grad.data.zero_()
out3.backward()
print(x.grad)
分离计算:y=f(x),u=f(y),若希望y作为一个常数处理,中断梯度追踪。可以调用.detach()将其从追踪记录中分离出来,这样就可以防止将来的计算被追踪,这样梯度就传不过去了。此外,还可以用with torch.no_grad()将不想被追踪的操作代码块包裹起来,这种方法在评估模型的时候很常用,因为在评估模型时,我们并不需要计算可训练参数(requires_grad=True)的梯度。
注:x.grad是和x同形的张量。
x.grad.zero_()
y = x * x
u = y.detach() # 返回新的tensor,从计算图中分离,不具有grad
z = u * x
z.sum().backward()
y1 = x ** 2
with torch.no_grad():
y2 = x ** 3 # y2没有梯度,y2.backward()会报错
y3 = y1 + y2 # y3的梯度 = y1的梯度 = 2x
y3.backward()
如果我们想要修改tensor的数值,但是又不希望被autograd记录(即不会影响反向传播),那么我么可以对tensor.data进行操作。
x = torch.ones(1,requires_grad=True)
print(x.data) # 还是一个tensor
print(x.data.requires_grad) # 但是已经是独立于计算图之外
y = 2 * x
x.data *= 100 # 只改变了值,不会记录在计算图,所以不会影响梯度传播
y.backward()
print(x) # 更改data的值也会影响tensor的值
print(x.grad)
z 不是一个标量,所以在调用backward时需要传入一个和z同形的权重向量进行加权求和得到一个标量。
简单来说就是为了避免向量(甚至更高维张量)对张量求导,而转换成标量对张量求导。我们不允许张量对张量求导,只允许标量对张量求导,求导结果是和自变量同形的张量。所以我们要把张量通过将所有张量的元素加权求和的方式转换为标量,假设y由自变量x计算而来,w是和y同形的张量,则y.backward(w)的含义是:先计算l = torch.sum(y * w),则l是个标量,然后求l对自变量x的导数。
x = torch.tensor([1.0, 2.0, 3.0, 4.0], requires_grad=True)
y = 2 * x
z = y.view(2, 2) # z不是一个标量
v = torch.tensor([[1.0, 0.1], [0.01, 0.001]], dtype=torch.float)
z.backward(v)
print(x.grad)