1.前言·
日常生活中的机器学习·
关键组件·
数据 ,转换数据的模型 ,目标函数 ,调整模型参数从而优化目标函数的算法
各种机器学习问题·
监督学习(supervised learning)·
回归(regression)
分类(classification)
标记问题
二元分类
多元分类 ==> 多标签分类(multi-label classification)
搜索
推荐系统(recommender system)
序列学习
无监督学习(unsupervised learning)·
聚类(clustering)
主成分分析(principal component analysis)
因果关系(causality)和概率图模型(probabilistic graphical models)
生成对抗性网络(generative adversarial networks)
与环境互动·
前面两者都是离线学习(offline learning),即不需要和环境交互,先训练好再启动
强化学习(reinforcement learning)·
深度Q网络(Q-network)
AlphaGo
在强化学习问题中,agent在一系列的时间步骤上与环境交互。 在每个特定时间点,agent从环境接收一些观察(observation),并且必须选择一个动作(action),然后通过某种机制(有时称为执行器)将其传输回环境,最后agent从环境中获得奖励(reward)。 此后新一轮循环开始,agent接收后续观察,并选择后续操作,依此类推。
深度学习之路·
成功案例·
2.预备知识·
2.1数据操作·
张量tensor,pytorch
和 tensorflow
中是 Tensor
,其实就是多维数组
1 2 3 4 5 6 7 8 9 10 import torchx=torch.arange(12 ) x.shape x.numel X=x.reshape(3 ,4 ) X=x.reshape(-1 ,4 ) X=torch.ones((2 ,3 )) X=torch.zeros(10 ) X=torch.randn(3 ,4 ) X=torch.tensor([1 ,2 ,3 ])
运算符·
1 2 3 4 5 6 7 8 9 10 11 12 13 x=torch.tensor([1 ,2 ,3 ]) y=torch.tensor([2 ,3 ,4 ]) x+y,x-y,x*y,x/y,x**y torch.exp(x) X=torch.arange(6 ,dtype=torch.float32).reshape(2 ,3 ) Y=torch.tensor([[1 ,2 ,3 ],[2 ,3 ,4 ]]) torch.cat((X,Y),dim=0 ),torch.cat((X,Y),dim=1 ) X==Y X.sum(dim=None , keepdim=False , dtype=None )
广播机制·
先复制元素扩展,再对同样形状的数组进行按元素操作
1 2 3 4 5 6 a=torch.arange(3 ).reshape(3 ,1 ) b=torch.arange(2 ).reshape(1 ,2 ) a+b
索引和切片·
1 2 3 X[-1 ],x[1 :3 ] X[1 ,2 ]=3 X[0 :2 ,:]=1
节省内存·
1 2 3 4 5 6 before = id(Y) Y = Y + X id(Y) == before before = id(Y) Y[:]=Y+X id(Y) == before
转换为其他python对象·
1 2 3 4 X=torch.tensor([1 ,2 ,3 ]) A = X.numpy() B = torch.tensor(A) B = torch.from_numpy(A)
2.2数据预处理·
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import osos.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,106000\n' ) f.write('4,NA,178100\n' ) f.write('NA,NA,140000\n' ) import pandas as pddf=pd.read_csv(data_file) inputs,outputs=data.iloc[:,0 :2 ],data.iloc[:,2 ] inputs = inputs.fillna(inputs.mean()) inputs = pd.get_dummies(inputs, dummy_na=True ) X, y = torch.tensor(inputs.values), torch.tensor(outputs.values) def drop_col (df) : num = df.isna().sum() num_dict = num.to_dict() max_key =max(num_dict,key=num_dict.get) del df[max_key] return df
2.3线性代数·
标量:单个数·
x,y,z
向量:标量组成的列表·
x ,y ,z
矩阵:标量组成的二维数组·
A,B,C
张量:标量组成的多维数组·
1 2 3 4 5 6 X=torch.range(24 ).reshape(3 ,3 ,4 ) A = torch.arange(20 , dtype=torch.float32).reshape(5 , 4 ) B = A.clone() A*B A+1 A*2
1 2 3 4 x=torch.arange(6 ,dtype=torch.float32).reshape(2 ,3 ) x.sum(axis=0 ) x.sum(axis=1 )
非降维求和·
1 2 3 4 5 6 x=torch.arange(6 ,dtype=torch.float32).reshape(2 ,3 ) sum_x = x.sum(axis=1 , keepdims=True ) sum_x x/sum_x x.cumsum(axis=0 )
$x,y\in Rd,x Ty=\sum_{i=1}^dx_iy_i$
加权和,计算夹角余弦
1 2 3 4 5 x=torch.range(4 ) y=torch.ones(4 ,dtype=torch.float32) torch.dot(x,y) torch.sum(x*y)
矩阵-向量积·
$A\in R^{m\times n},x\in R^n$
相乘得到$B^m$ 即长度为m的向量
1 2 3 4 A=torch.arange(6 ).reshape(2 ,3 ) x=torch.tensor([2 ,4 ,5 ]) torch.mv(A,x)
矩阵乘法·
$A\in R^{n\times k},B\in R^{k\times m}$
$AB\in R^{n\times m}$
可以理解为是A的n个行向量 与B的m个列向量 分别相乘
1 2 3 4 5 6 7 8 9 10 A=torch.range(15 ).reshape(5 ,3 ) B=torch.range(12 ).reshape(3 ,4 ) torch.mm(A,B) ''' [[ 20, 23, 26, 29], [ 56, 68, 80, 92], [ 92, 113, 134, 155], [128, 158, 188, 218], [164, 203, 242, 281]] '''
范数 norm·
将向量x 映射到标量的函数$f$
满足三个性质
$f(\alpha x)=|\alpha|f(x)$
$f(x+y)\leq f(x)+f(y)$,三角不等式
$f(x)\ge 0$,当且仅当x 分量全为0时相等
$L_2$范数:$||x||2=\sqrt{(\sum {i=1}nx_i 2)}$ 一般可以简写为$||x||$
1 2 u=torch.tensor([3.0 ,4.0 ]) torch.norm(u)
$L_1$范数:$||x||1=\sum {i=1}^n|x_i|$
1 2 u=torch.tensor([3.0 ,4.0 ]) torch.abs(u).sum()
$L_p$范数:$||x||p=(\sum {i=1}n|x_i| p)^{1/p}$
矩阵的$Frobenius$范数(Frobenius norm)是矩阵元素平方和的平方根
1 torch.norm(torch.ones((4 , 9 )))
深度学习中的目标常表达为范数
2.4微积分·
2.5自动微分·
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 import torchx = torch.arange(4.0 ) x.requires_grad_(True ) y=10 *torch.dot(x,x) y.backward() x.grad x.grad==20 *x x.grad.zero_() y = x.sum() y.backward() x.grad x.grad.zero_() y = x * x y.sum().backward() x.grad x.grad.zero_() y = x * x u = y.detach() z = u * x z.sum().backward() x.grad == u 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()
2.6概率·
2.7查阅⽂档·
3.线性神经网络(LNN)·
线性回归·
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 40 41 42 43 44 45 46 47 48 from torch.utils import datadef synthetic_data (w, b, num_examples) : """生成y=Xw+b+噪声 Defined in :numref:`sec_linear_scratch`""" X = torch.normal(0 , 1 , (num_examples, len(w))) y = torch.matmul(X, w) + b y += torch.normal(0 , 0.01 , y.shape) return X, torch.reshape(y, (-1 , 1 )) true_w = torch.tensor([2 , -3.4 ]) true_b = 4.2 features, labels = synthetic_data(true_w, true_b, 1000 ) def load_array (data_arrays, batch_size, is_train=True) : """构造一个PyTorch数据迭代器。""" dataset = data.TensorDataset(*data_arrays) return data.DataLoader(dataset, batch_size, shuffle=is_train) batch_size = 10 data_iter = load_array((features, labels), batch_size) from torch import nnnet = nn.Sequential(nn.Linear(2 , 1 )) net[0 ].weight.data.normal_(0 , 0.01 ) net[0 ].bias.data.fill_(0 ) loss = nn.MSELoss() trainer = torch.optim.SGD(net.parameters(), lr=0.03 ) num_epochs = 3 for epoch in range(num_epochs): for X, y in data_iter: l = loss(net(X) ,y) trainer.zero_grad() l.backward() trainer.step() l = loss(net(features), labels) print(f'epoch {epoch + 1 } , loss {l:f} ' )
softmax回归·
$softmax$ 运算:$y_j=\frac {exp(o_j)} {\sum_{k=1}^nexp(o_k)}$
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 40 41 42 43 44 45 46 47 48 49 50 51 52 import torchvision,torchfrom torch import nnfrom torchvision import transformsdef get_dataloader_workers () : """使用4个进程来读取数据""" return 4 def load_data_fashion_mnist (batch_size, resize=None) : """下载Fashion-MNIST数据集,然后将其加载到内存中""" trans = [transforms.ToTensor()] if resize: trans.insert(0 , transforms.Resize(resize)) trans = transforms.Compose(trans) mnist_train = torchvision.datasets.FashionMNIST( root="../data" , train=True , transform=trans, download=True ) mnist_test = torchvision.datasets.FashionMNIST( root="../data" , train=False , transform=trans, download=True ) return (data.DataLoader(mnist_train, batch_size, shuffle=True , num_workers=get_dataloader_workers()), data.DataLoader(mnist_test, batch_size, shuffle=False , num_workers=get_dataloader_workers())) batch_size = 256 train_iter, test_iter = load_data_fashion_mnist(batch_size) net = nn.Sequential(nn.Flatten(), nn.Linear(784 , 10 )) def init_weights (m) : if type(m) == nn.Linear: nn.init.normal_(m.weight, std=0.01 ) net.apply(init_weights) loss = nn.CrossEntropyLoss(reduction='none' ) trainer = torch.optim.SGD(net.parameters(), lr=0.1 ) num_epochs = 10 for i in range(num_epochs): for X,y in train_iter: l=loss(net(X),y) trainer.zero_grad() l.mean().backward() trainer.step() loss_sum = 0 cnt = 0 for X,y in test_iter: loss_sum+=loss(net(X),y).sum() cnt+=1 print("epoch%d" %i,":" ,float(loss_sum/cnt))
4.多层感知机(MLP)·
多层感知机·
1 2 3 4 5 6 7 net = nn.Sequential( nn.Flatten(), nn.Linear(784 ,256 ), nn.ReLU(), nn.Linear(256 ,10 ) )
激活函数·
ReLU函数·
$ReLU(x)=max(x,0)$
sigmoid函数·
$sigmoid(x)=\frac {1} {1+exp(-x)}$
tanh函数·
$tanh(x)=\frac {1-exp(-2x)} {1+exp(-2x)}$
模型选择、欠拟合和过拟合·
权重衰减·
给权重加上惩罚项,也称为$L_2$正则化
最简单的方式:直接使用权重向量的某个范数来度量复杂性,比如二范数
即损失变为:$L(w,b)+\frac \lambda 2 ||\omega||^2$
1 2 3 4 5 wd=3 trainer=torch.optim.SGD([ {"params" :net[0 ].weight,'weight_decay' :wd}, {"params" :net[0 ].bias}],lr=lr)
暂退法·
扰动或者随机抛弃一些隐藏层的输出(即下一层输入)降低影响
1 2 3 4 5 6 7 8 9 10 net = nn.Sequential( nn.Flatten(), nn.ReLU(), nn.Dropout(0.2 ) nn.Linear(784 ,256 ), nn.ReLU(), nn.Dropout(0.2 ) nn.Linear(256 ,10 ) )
前向传播,反向传播与计算图·
数值稳定性和模型初始化·
环境与分布偏移·
5.深度学习计算·
层和块·
自定义块·
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from torch.nn.modules.flatten import Flattenimport torchvision,torchfrom torch import nnfrom torch.nn import functional as Fclass MLP (nn.Module) : def __init__ (self) : super().__init__() self.hidden=nn.Linear(20 ,256 ) self.output=nn.Linear(256 ,10 ) def forward (self,X) : return self.output(F.relu(self.hidden(X))) mlp=MLP() X=torch.arange(20 ,dtype=torch.float32) mlp(X)
顺序块·
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class MySequential (nn.Module) : def __init__ (self, *args) : super().__init__() for idx, module in enumerate(args): self._modules[str(idx)] = module def forward (self, X) : for block in self._modules.values(): X = block(X) return X net = MySequential(nn.Linear(20 , 256 ), nn.ReLU(), nn.Linear(256 , 10 )) net(X)
前向传播函数中执行代码·
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class MyForwardSequential (nn.Module) : def __init__ (self, *args) : super().__init__() self.rand_weight = torch.rand((20 , 20 ), requires_grad=False ) self.linear = nn.Linear(20 , 20 ) def forward (self, X) : X=self.linear(X) X=F.relu(X) X=self.linear(X) while X.sum()>1 : X/=2 return X.sum() net = MyForwardSequential() net(X)
参数管理·
参数访问·
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 net = nn.Sequential(nn.Linear(4 , 8 ), nn.ReLU(), nn.Linear(8 , 1 )) net[2 ].state_dict() net[2 ].bias net[2 ].bias.data net[2 ].bias.grad net[2 ].weight net[2 ].weight.data net[2 ].weight.grad print(*[(name, param.shape) for name, param in net[2 ].named_parameters()]) print(*[(name, param.shape) for name, param in net.named_parameters()]) """ ('weight', torch.Size([1, 8])) ('bias', torch.Size([1])) ('0.weight', torch.Size([8, 4])) ('0.bias', torch.Size([8])) ('2.weight', torch.Size([1, 8])) ('2.bias', torch.Size([1])) """
参数初始化·
内置初始化·
1 2 3 4 5 6 7 8 9 10 11 12 def init_normal (m) : if type(m) == nn.Linear: nn.init.normal_(m.weight, mean=0 , std=0.01 ) nn.init.zeros_(m.bias) nn.init.constant_(m.weight,1 ) nn.init.xavier_uniform_(m.weight) net.apply(init_normal) net[0 ].apply(xavier) net[2 ].apply(init_42) print(net[0 ].weight.data[0 ]) print(net[2 ].weight.data)
延后初始化·
输入维度与前一层输出维度都延后到第一次计算时自动进行推断
自定义层·
不带参数·
1 2 3 4 5 6 7 8 9 10 11 class CenteredLayer (nn.Module) : def __init__ (self) : super().__init__() def forward (self, X) : return X - X.mean() net=nn.Sequential( nn.Linear(20 ,12 ), CenteredLayer(), nn.Dropout(0.8 ), nn.ReLU() )
带参数·
1 2 3 4 5 6 7 class MyLinear (nn.Module) : def __init__ (self,innum,outnum) : super().__init__() self.weight=nn.Parameter(torch.randn(innum,outnum)) self.bias=nn.Parameter(torch.randn(outnum)) def forward (self,X) : return torch.matmul(X,self.weight.data)+self.bias.data
读写文件·
加载和保存张量·
1 2 3 4 5 6 7 x=torch.arange(24 ) torch.save(x,'x-tensor' ) x=torch.load('x-tensor' ) torch.save([x,y],'x-tensor' ) x,y=torch.load('x-tensor' ) torch.save({'x' :x,'y' :y},'dict' ) mydict=torch.load('dict' )
加载和保存模型参数·
1 2 3 4 net=MLP() torch.save(net.state_dict(),'mlp.params' ) net.load_state_dict(torch.load('mlp.params' )) net.eval()
GPU·
计算设备·
1 2 3 4 5 6 7 8 9 10 import torchfrom torch import nntorch.device('cpu' ) torch.device('cuda' ) torch.cuda.device_count() def try_all_gpus () : devices=[torch.device(f'cuda:{i} ' ) for i in range(torch.cuda.device_count())] return devices if devices else [torch.device('cpu' )] def try_gpu (i=0 ) : return torch.device('cpu' ) if torch.cuda.device_count()<i+1 else torch.device(f'cuda:{i} ' )
张量与GPU·
1 2 3 4 5 6 7 8 x=torch.tensor([1 ,2 ,3 ]) x.device x=torch.ones(2 ,3 ,device=try_gpu()) x=torch.ones(2 ,3 ,device=try_gpu(0 )) x=torch.ones(2 ,3 ,device=try_gpu(1 )) Z=X.cuda(1 )
神经网络与GPU·
1 2 3 net=nn.Sequential(nn.Linear(3 ,1 )) net=net.to(device=(try_gpu()))
6.卷积神经网络(CNN)·
卷积基础·
卷积核·
沃尔多检测器—空间不变性!
从二维图像输入,即二维张量$m*n$开始,则如果隐藏层是全连接层,就会需要有$m2n 2$个参数,此时的计算公式为:
$H_{i,j}=U_{i,j}+\sum_k\sum_lW_{i,j,k,l}X_{k,l}=U_{i,j}+\sum_{a}\sum_bV_{i,j,a,b}X_{i+a,j+b}$
此时利用图像的不变性原则,即对于同一个物体在不同处检测结果应该一致,所以有$V_{i,j,a,b}=V_{a,b}$
且$U$为常数,设为$u$
所以可以简化为$H_{i,j}=u+\sum_{a}\sum_bV_{a,b}X_{i+a,j+b}$,相当于对其$X_{i,j}$周边的像素进行加权得到$H_{i,j}$
同时,又考虑到局部性,因此最终的$H_{i,j}=u+\sum_{-\Delta}{\Delta}\sum_{-\Delta} {\Delta}V_{a,b}X_{i+a,j+b}$
这里的$V$是卷积核(convolution kernel),或者说是滤波器(filter)
输入的图像数据一般是三通道的,即红绿蓝,而维数则是3维张量。隐藏层一般也设置成3维张量,但由于需要学习更多的隐藏表示,每个隐藏表示是一系列具有二维张量的通道,比如纹理,边缘等等,有时也称为特征映射,被称为feature maps,为了表示多个隐藏表示,所以需要给卷积核增加一个维度,即
$H_{i,j,d}=\sum_{-\Delta}{\Delta}\sum_{-\Delta} {\Delta}\sum_cV_{a,b,c,d}X_{i+a,j+b,c}$
图像卷积·
互相关运算·
比如以下的卷积计算结果:
1 2 3 4 5 6 7 8 9 10 [[1 ,2 ,3 ], [4 ,5 ,6 ], [7 ,8 ,9 ]] [[0 ,1 ], [2 ,3 ]] [[25 ,31 ], [43 ,49 ]]
输入为$nm$,卷积核为$h w$,则卷积相当于是一个正方块在输入的正方形进行移动计算,并到达自己能够到达的每个位置,最后的卷积结果为$(n-h+1)*(m-w+1)$
卷积层·
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 conv2d = nn.Conv2d(1 ,1 , kernel_size=(1 , 2 ), bias=False ) X = X.reshape((1 , 1 , 6 , 8 )) Y = Y.reshape((1 , 1 , 6 , 7 )) lr = 3e-2 for i in range(10 ): Y_hat = conv2d(X) l = (Y_hat - Y) ** 2 conv2d.zero_grad() l.sum().backward() conv2d.weight.data[:] -= lr * conv2d.weight.grad if (i + 1 ) % 2 == 0 : print(f'epoch {i+1 } , loss {l.sum():.3 f} ' )
特征映射与感受野·
对于某⼀层的任意元素x,其感受野(receptive field)是指在前向传播期间可能影响x计算的所有元素(来⾃所有先前层)
填充与步幅·
对输入做填充处理从而捕捉边缘信息
假设添加$p_h$行填充和$p_w$列填充,填充的内容都是上下差不多各一半,左右差不多各一半,则输出形状为 $(n_h-k_h+p_h+1)\times (n_w-k_w+p_w+1)$
所以一般设置 $p_h=k_h-1,p_w=k_w-1$ 这样就可以让输入输出形状相同
同时,如果满足:
卷积核大小为奇数
所有边的填充行数与列数相同
输出与输入具有相同高度和宽度
则可以得出:输出$Y[i,j]$是通过输入$X[i,j]$为中心,与卷积核进行互相关计算得到的。
1 2 conv2d = nn.Conv2d(1 , 1 , kernel_size=(5 , 3 ), padding=(2 , 1 )) conv2d = nn.Conv2d(1 , 1 , kernel_size=(5 , 3 ), padding=3 )
按照一定步幅进行卷积,从而输入的宽度高度减少一定比例
1 2 conv2d = nn.Conv2d(1 , 1 , kernel_size=3 , padding=1 , stride=2 ) conv2d = nn.Conv2d(1 , 1 , kernel_size=3 , padding=1 , stride=(2 ,3 ))
变为:$\lfloor(n_k-k_h+p_h+s_h)/s_h\rfloor\times \lfloor(n_w-k_w+p_w+s_w)/s_w\rfloor$
后面的池化也是类似的
多输⼊多输出通道·
多输入通道·
每个通道计算完成之后将每个通道卷积得到的结果相加
多输出通道·
在之前多输入基础上,加上一个维度来产生多通道的输出,相当于每一个通道自己有一组核函数
1 K = torch.stack((K, K + 1 , K + 2 ), 0 )
汇聚层·
汇聚层目的:降低卷积层对位置 的敏感性,降低对空间降采样 表示的敏感性
最大汇聚层与平均汇聚层·
不包含参数,具有确定性,即直接计算汇聚窗口中的所有元素的最大值或平均值
同样,汇聚层也具有填充和步幅
1 2 pool2d = nn.MaxPool2d(3 ,padding=1 , stride=2 ) pool2d = nn.MaxPool2d(3 ,padding=(1 ,2 ), stride=(2 ,3 ))
多通道时,每个输入通道单独运算而不会对结果汇总,因此输出的通道数量与输入通道数相同
卷积神经网络(LeNet)·
Lenet包含两个部分:
卷积编码器:两个卷积块组成
每个卷积块包含1个卷积层,1个sigmoid激活函数,还有平均汇聚层
全连接层密集块:三个全连接层组成
网络结构·
1 2 3 4 5 6 7 8 9 10 net = nn.Sequential( nn.Conv2d(1 , 6 , kernel_size=5 , padding=2 ), nn.Sigmoid(), nn.AvgPool2d(kernel_size=2 , stride=2 ), nn.Conv2d(6 , 16 , kernel_size=5 ), nn.Sigmoid(), nn.AvgPool2d(kernel_size=2 , stride=2 ), nn.Flatten(), nn.Linear(16 * 5 * 5 , 120 ), nn.Sigmoid(), nn.Linear(120 , 84 ), nn.Sigmoid(), nn.Linear(84 , 10 ))
7.现代卷积神经网络·
AlexNet。它是第⼀个在⼤规模视觉竞赛中击败传统计算机视觉模型的⼤型神经⽹络;
使⽤重复块的⽹络(VGG)。它利⽤许多重复的神经⽹络块;
⽹络中的⽹络(NiN)。它重复使⽤由卷积层和1 × 1卷积层(⽤来代替全连接层)来构建深层⽹络;
含并⾏连结的⽹络(GoogLeNet)。它使⽤并⾏连结的⽹络,通过不同窗⼝⼤⼩的卷积层和最⼤汇聚层来并⾏抽取信息;
残差⽹络(ResNet)。它通过残差块构建跨层的数据通道,是计算机视觉中最流⾏的体系架构;
稠密连接⽹络(DenseNet)。它的计算成本很⾼,但给我们带来了更好的效果。