机器学习前沿实验要点
1: 数据标准化写法
1 X=(X-np.mean(X,axis=0 ))/X.std()
其中 X.std()
表示标准差, axis=0
表示按列计算均值.
2: 用 Tensorflow 写神经网络的一般流程.
3: RNN 及其变种用做分类的注意事项.
RNN 所处理的数据为(时间)序列数据, 形如 .
那么 , 记
, , , 表示为单向 RNN
或者双向 RNN,
表示隐藏层数量 , , 表示分类问题中总的类别个数.
下面是一个单层单向 GRU 的例子, 即 .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class GRUModel (nn.Module): def __init__ (self, input_size, hidden_size, output_size ): super (GRUModel, self).__init__() self.hidden_size = hidden_size self.gru = nn.GRU(input_size, hidden_size) self.fc = nn.Linear(hidden_size, output_size) def forward (self, input , hidden ): output, hidden = self.gru(input , hidden) output = self.fc(output) return output, hidden def initHidden (self ): return torch.zeros(1 , batch_size, self.hidden_size) net=GRUModel(X,init_hidden)
如第 14 行定义网络, 需要两个参数, 分别是输入数据 , 形状为 ; 初始隐藏层
init_hidden
, 形状为 .
forward()
函数中的 output
的形状为 , 表示训练完成后每个时间步 GRU
的输出; hidden
的形状为 ,
包括每个隐藏层最后一个时间步的状态.
因此最终计算 loss 时只需要 output
的最后一个时间步的输出, 也就是只需要一个形状为 的数据和长度为 的向量计算交叉熵.
下面是一个多层 LSTM 的例子, 这里处理了输出, 只要最后一个时间步,
注意在 torch.squeeze(output[:, -1:, :], dim=1)
中加了
dim=1
保证只在
的那一位进行 squeeze, 防止 时将
output
squeeze 成了一个一维张量而导致计算交叉熵时出错:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class MLLSTM (nn.Module): def __init__ (self, input_size, hidden_size, num_layers, output_size ): super (MLLSTM, self).__init__() self.hidden_size = hidden_size self.num_layers = num_layers self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True , bidirectional=False ) self.fc = nn.Linear(hidden_size, output_size) self.dropout = nn.Dropout(p=0.5 ) def forward (self, input , hidden ): batch_size = input .shape[0 ] hidden = self.initHidden(batch_size) output, _ = self.lstm(input , hidden) output = self.fc(output) output = self.dropout(output) return torch.squeeze(output[:, -1 :, :], dim=1 ) def initHidden (self, batch_size ): return (torch.ones(self.num_layers * 1 , batch_size, self.hidden_size), torch.ones(self.num_layers * 1 , batch_size, self.hidden_size))
人工智能综合实验要点
一般性问题
网络定义部分
nn.BatchNorm1d()
和 nn.BatchNrom2d()
model.train()
和
model.eval()
.
with torch.no_grad()
神经网络的初始化
当定义一个网络模型后, 参数的默认初始化方式为随机初始化, 除此之外,
还有下面的初始化方式:
全零或等值
正态
均匀
Xavier . 适用于各种激活函数, 但在 上表现良好而在 ReLU
上表现不好.
Kaiming . Xavier 的改进, 在 ReLU 上表现良好.
1 2 3 4 5 6 7 8 for m in self._net.modules(): if isinstance (m,nn.Conv2d): nn.init.kaiming_normal_(m.weight) nn.init.constant_(m.bias,0 ) elif isinstance (m,nn.Linear): nn.init.normal_(m.weight,0 ,0.01 ) nn.init.constant_(m.bias,0 )
训练与测试部分
实验一
batch, batch_size, epoch,
train_iter
为了加速训练神经网络往往使用 batch 的方法来处理数据.
具体而言是将训练数据集以 batch_size 的大小划分为若干个批次, 得到
train_iter, train_iter 是形如
的张量, 可以使用
的形式来获取每个批次的输入 和
label , 其中 是数量为 batch_size 维度为 的数据形成的张量, 是长度为 batch_size 的向量.
每个 epoch 都要按照 train_iter
迭代遍历所有的数据 .
每个 batch
的数据具体是如何处理的?
Softmax回归
1: 将图片展开成向量.
2: 一个神经网络类都包括哪些内容? 每部分内容的含义是什么?
3: 一个一般的训练神经网络的结构是什么?
下面是一个比较完整训练与测试的函数, 函数的参数为:
1 2 3 4 5 6 7 8 9 10 11 train_and_test( net, num_epochs, train_dataset, test_dataset, batch_size, optimizer, loss, init_hidden, num_workers=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 40 41 42 43 44 import torchdevice=torch.device("cuda:0" if torch.cuda.is_available() else "cpu" ) def train_and_test (net,num_epochs,train_dataset,test_dataset,batch_size,optimizer,loss,init_hidden,num_workers=0 ): net.to(device) train_iter = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True , num_workers=num_workers) test_iter = torch.utils.data.DataLoader(test_dataset, batch_size=batch_size, shuffle=False , num_workers=num_workers) losses = [] test_accs = [] train_accs=[] n_train=len (train_dataset) n_test=len (test_dataset) for epoch in range (num_epochs): train_l_sum, train_acc= 0.0 , 0.0 correct=0 for X, y in train_iter: y=y.long() X, y = X.to(device), y.to(device) output=net(X,init_hidden) optimizer.zero_grad() l=loss(output,y) l.backward() train_l_sum+=l.item() optimizer.step() _,predicted=torch.max (output.data,1 ) correct+=((predicted==y).sum ().item()) train_acc=correct / n_train train_accs.append(train_acc) correct=0 with torch.no_grad(): for X,y in test_iter: y=y.long() X, y = X.to(device), y.to(device) output=net(X,init_hidden) l=loss(output,y) _,predicted=torch.max (output.data,1 ) correct+=((predicted==y).sum ().item()) test_acc=correct/n_test test_accs.append(test_acc) losses.append(train_l_sum / n_train) print ('epoch %d, loss %.4f, train acc %.3f, test acc %.3f' % (epoch + 1 , train_l_sum / n_train, train_acc, test_acc)) torch.cuda.empty_cache() return losses,train_accs,test_accs
nn.ReLU(inplace=True)
创建了一个 ReLU
激活函数的实例,并将其赋值给了
self.relu
。inplace=True
的意思是将计算结果直接覆盖原有的内存空间,以节省内存。这样做可以在反向传播时节省一些内存开销。
实验二
LeNet 和 ResNet 实现 CIFAR-10 数据分类.
1: nn.Conv2d
参数, 一般只确定下面 5 个参数.
1 2 3 4 5 6 7 class Conv2d ( in_channels: int , out_channels: int , kernel_size: _size_2_t, stride: _size_2_t = 1 , padding: _size_2_t | str = 0 , )
2: LeNet 结构.
1 [conv -> ReLU -> pooling]*2 -> fc*2
第一层 conv 参数: [cIn : 3, cOut : 6, kernel : 5]
第一层 pooling 参数: [width : 2, height : 2]
第二层 conv 参数: [cIn : 6, cOut : 16, kernel : 5]
第二层 pooling 参数: 同第一层
两层 fc: .
1 2 3 4 5 6 7 8 torch.nn.MaxPool2d( kernel_size, stride=None , padding=0 , dilation=1 , return_indices=False , ceil_mode=False )
下面是 LeNet 的 Pytorch 实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class LeNet (nn.Module): ''' Input Size: [N, 3, 32, 32] ''' def __init__ (self, ): super (LeNet, self).__init__() self._net=nn.Sequential(*[ nn.Conv2(3 ,6 ,5 ), nn.ReLU(), nn.MaxPool2d(2 ,2 ), nn.Conv2(6 ,16 ,5 ), nn.ReLU(), nn.MaxPool2d(2 ,2 ), nn.Flatten(), nn.Linear(16 *5 *5 ,120 ), nn.Linear(120 ,10 ) ]) def forward (self,x ): return self._net(x)
实验三
RNN 实现文本翻译.
seq2seq 结构/Encoder-Decoder
结构
Teacher Forcing Training
nn.Embedding()
的两个参数为词汇大小和嵌入维度,
词汇大小指的是文本数据中不同单词的数量 ,
嵌入维度指的是将词汇映射到的密集向量的维度.
nn.RNN()
的参数为
1 2 3 4 5 6 7 8 9 10 11 nn.RNN( input_size, hidden_size, num_layers=1 , nonlinearity=tanh, bias=True , batch_first=False , dropout=0 , bidirectional=False )
EncoderRNN
的代码如下,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class EncoderRNN (nn.Module): def __init__ (self, input_size, hidden_size ): super (EncoderRNN, self).__init__() self.hidden_size = hidden_size self.embedding = nn.Embedding(input_size, hidden_size) self.rnn = nn.RNN(hidden_size,hidden_size) def forward (self, input , hidden ): input_code=self.embedding(input ) output=input_code.view(1 ,1 ,-1 ) output,hidden=self.rnn(output,hidden) return output,hidden def initHidden (self ): return torch.zeros(1 , 1 , self.hidden_size, device=device)
设隐藏层维度为 hidden_size
1 encoder=EncoderRNN(input_lang.n_words, hidden_size)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class DecoderRNN (nn.Module): def __init__ (self, hidden_size, output_size ): super (DecoderRNN, self).__init__() self.hidden_size = hidden_size self.embedding = nn.Embedding(output_size, hidden_size) self.rnn = nn.RNN(hidden_size,hidden_size) self.out = nn.Linear(hidden_size, output_size) self.softmax = nn.LogSoftmax(dim=1 ) def forward (self, input , hidden ): input_code=self.embedding(input ) output=F.relu(input_code.view(1 ,1 ,-1 )) output,hidden=self.rnn(output,hidden) output = self.softmax(self.out(output[0 ])) return output,hidden def initHidden (self ): return torch.zeros(1 , 1 , self.hidden_size, device=device)
1 decoder1 = DecoderRNN(hidden_size, output_lang.n_words)
实验四:AutoEncoder
1: 对于具有 encoder-decoder 的结构的网络,
定义优化器时采用下面的方式:
1 optimizer = torch.optim.Adam(list (encoder.parameters()) + list (decoder.parameters()), lr)
就是将编码器和解码器的参数加起来.
2: nn.BatchNorm1d(dHidden)
的含义: 归一化数据,
可以加速训练.
3: 上采样与下采样.
统计学习实验要点
不用写代码太好力, 学一下别人的代码
1:
magic command, 可以让画的图直接嵌入到 notebook 的输出单元格中,
不用显式调用 plt.show()
让图形显示.
2: data.head()
可以显示前五行数据, data
是
pd
格式的数据.
3: data.shape
不用 print, 直接就能输出到 notebook
的单元格中.
4: axis=0
表示按行操作. axis=1
分别表示按列操作.
5: sklearn 的模型的一般使用格式:
1 2 3 4 5 6 7 8 9 10 from sklearn import ModelName() model = ModelName() model.fit(X_train, y_train) y_pred = model.predict(X_test) print ('R^2:' ,metrics.r2_score(y_train, y_pred))print ('Adjusted R^2:' ,1 - (1 -metrics.r2_score(y_train, y_pred))*(len (y_train)-1 )/(len (y_train)-X_train.shape[1 ]-1 ))print ('MAE:' ,metrics.mean_absolute_error(y_train, y_pred))print ('MSE:' ,metrics.mean_squared_error(y_train, y_pred))print ('RMSE:' ,np.sqrt(metrics.mean_squared_error(y_train, y_pred)))
6: 视觉任务中, 显示数据集的若干张图片的函数:
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 def show_samples (batch_img, batch_label=None , num_samples=16 ): sample_idx = 0 total_col = 4 total_row = math.ceil(num_samples / 4 ) col_idx = 0 row_idx = 0 fig, axs = plt.subplots(total_row, total_col, figsize=(15 , 15 )) while sample_idx < num_samples: img = batch_img[sample_idx] img = img.view(3 , -1 ) * channel_std.view(3 , -1 ) + channel_mean.view(3 , -1 ) img = img.view(3 , 224 , 224 ) img = img.permute(1 , 2 , 0 ) axs[row_idx, col_idx].imshow(img) if batch_label != None : axs[row_idx, col_idx].set_title(dataset.label_idx2name[(batch_label[sample_idx])]) sample_idx += 1 col_idx += 1 if col_idx == 4 : col_idx = 0 row_idx += 1
7: 一个预训练的ViT使用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class PretrainViT (nn.Module): def __init__ (self ): super (PretrainViT, self).__init__() model = models.vit_l_16(pretrained=True ) num_classifier_feature = model.heads.head.in_features model.heads.head = nn.Sequential( nn.Linear(num_classifier_feature, 120 ) ) self.model = model for param in self.model.named_parameters(): if "heads" not in param[0 ]: param[1 ].requires_grad = False def forward (self, x ): return self.model(x)
"分类头"(classification
head)是指深度学习模型中的一组层,用于将模型的中间表示转化为最终的分类输出,
一般是最后一层.
8: sklearn 的多项式回归也是用线性回归实现的, 具体而言,
1 2 3 4 5 6 7 8 from sklearn.preprocessing import PolynomialFeaturesdegree=2 poly=PolynomialFeatures(degree=degree) X_train_poly=poly.fit_transform(X_train) X_test_poly=poly.transform(X_test) model=LinearRegression() model.fit(X_train_poly, y_train)
MLP原理:
多层感知机要学习的参数是一系列权重矩阵 , 使得 , 然后优化 .
一些报错
1:
1 RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn
对 requires_grad
的值为 False
的变量求导导致的报错, 需要找到哪些变量不能求导, 再让
x.requires_grad=True
即可, 注意对于
x.requires_grad
为 True
的变量不要赋值,
否则会报错
1 RuntimeError: you can only change requires_grad flags of leaf variables.
因此正确的方式是
1 2 if x.requires_grad==False : x.requires_grad=True
一篇可能比较详细的对自动求导做介绍的文章: pytorch自动求导Autograd系列教程(一)
- 知乎 (zhihu.com)
cuda out of memory
下面的代码可以释放掉已分配但未使用的内存。
1 2 import torchtorch.cuda.empty_cache()
下面的代码可以删除所有的张量,可能要慎重使用: