ML2021_Spring_HW02心得
作业1 Phoneme Classification
助教视频讲解
本次作业助教的视频讲解(需要翻墙)
课程作业代码和作业PPT
本次作业的目的
- 学习一个基本的神经网络的搭建,调参过程
- 学习pytorch的使用。
- 将simple baseline模型调整,使得损失进一步下降。
任务说明
本次作业需要做的是一个phoneme的分类任务,这是一个39分类的任务,输入是一段语音信号,经过一些特征处理等操作(例如FFT等),提取需要的特征,进而进行接下来的分类任务。
sample code 分析
先说一下整个数据被处理的过程。输入数据train的维度为:1229932×429(样本数×特征数,特征用词不一定准确),所以,神经网络的第一层输入的维度为429。之后,由于VAI_RATIO=0.2,所以validation_set为245987×429,train_set的维度为983945×429。由于BATCH_SIZE的大小为64,所以经过dataloader处理后每个Batch的为64×429。
数据分析
如果下载到ML2021_Spring的课件,助教是有给提供样例的。
本文所用的数据集可以到它给的Kaggle链接上去下载,直接下载到的课件里是没有附带数据集的。
直接下载下来的数据集包含三个文件。
数据准备
.npy文件是numpy专用的二进制文件。
保存和加载可以直接用numpy的处理函数。
import numpy as np
data_root='./timit_11/'
train=np.load(data_root+'train_11.npy')
train_label=np.load(data_root+'train_label_11.npy')
test=np.load(data_root+'test+11.npy')
创建dataset以及data loader
这一步的主要目的是为了将ndarray转换成Tensor,然后用pytorch自带的Dataloader将dataset按batchsize划分成一个个batch。将数据集处理成dataset的形式,这个函数一般需要自己定义。该sample code划分validation的方式是取训练集中一定比例的数据。两步处理的方式:
from torch.utils.data import Dataset
#Class TIMITDataset 继承了torch里面的Dataset
#在这个function中define了三个函数,初始化,获取数据,以及获取数据长度的变量。
#下面的train_set 以及val_set都是Dataset类型
class TIMITDataset(Dataset):
def __init__(self,X,y=None):
self.data=torch.from_numpy(X).float() #creates a Tensor from a numpy.ndarray
if y is not None:
y=y.astype(np.int)
self.label=torch.LongTensor(y) #tensor直接不可以转换为longtensor,必须先转换为ndarray,再转换为longtensor
else:
self.label=None
VAL_RATIO = 0.2
percent = int(train.shape[0] * (1 - VAL_RATIO))
train_x, train_y, val_x, val_y = train[:percent], train_label[:percent], train[percent:], train_label[percent:]
print('Size of training set: {}'.format(train_x.shape))
print('Size of validation set: {}'.format(val_x.shape))
def __getitem__(self, idx):
if self.label is not None:
return self.data[idx],self.label[idx]
else:
return self.data[idx]
def __len__(self):
return len(self.data)
#Split the labeled data into a training set and a validation set.
# Create a data loader from the dataset.
BATCH_SIZE=64
from torch.utils.data import DataLoader
train_set=TIMITDataset(train_x,train_y)
val_set=TIMITDataset(val_x,val_y)
train_loader=DataLoader(train_set,batch_size=BATCH_SIZE,shuffle=True) #only shuffle the training data
val_loader=DataLoader(val_set,batch_size=BATCH_SIZE,shuffle=True)
这里有必要介绍一下dataloader这个函数。
Data loader. Combines a dataset and a sampler, and provides an iterable over the given dataset.
数据加载程序。组合数据集和采样器,并提供给定数据集的可迭代对象。
dataset (Dataset) – dataset from which to load the data.
batch_size (int, optional) – how many samples per batch to load (default: 1).
shuffle (bool, optional) – set to True to have the data reshuffled at every epoch (default: False).
Create Model
Sample code给的是一个3个layer,3个act_fn的神经网络。Classifier可以直接继承nn.Module
class Classifier(nn.Module):
def __init__(self):
super(Classifier, self).__init__()
self.layer1 = nn.Linear(429, 1024)
self.layer2 = nn.Linear(1024, 512)
self.layer3 = nn.Linear(512, 128)
self.out = nn.Linear(128, 39)
self.act_fn = nn.Sigmoid()
def forward(self, x):
x = self.layer1(x)
x = self.act_fn(x)
x = self.layer2(x)
x = self.act_fn(x)
x = self.layer3(x)
x = self.act_fn(x)
x = self.out(x)
return x
配置训练参数
个人觉得把有关训练参数,例如epoch,batch_size,learning_rate等写在一个config里比较好,HW01就是这样写的。方便之后调整
#cuda可用就用GPU
def get_device():
return 'cuda' if torch.cuda.is_available() else 'cpu'
#这个函数应该是都一样的,为了之后的reproducibility,固定随机数种子
def same_seeds(seed):
torch.manual_seed(seed)
if torch.cuda.is_available():
torch.cuda.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
np.random.seed(seed)
torch.backends.cudnn.benchmark = False
torch.backends.cudnn.deterministic = True
same_seeds(0)
# get device
device = get_device()
print(f'DEVICE: {device}')
# training parameters
num_epoch = 20 # number of training epoch
learning_rate = 0.0001 # learning rate
# the path where checkpoint saved
model_path = './model.ckpt'
# create model, define a loss function, and optimizer
model = Classifier().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
训练过程
best_acc = 0.0
for epoch in range(num_epoch):
train_acc = 0.0
train_loss = 0.0
val_acc = 0.0
val_loss = 0.0
# training
model.train() # set the model to training mode
for i, data in enumerate(train_loader):
inputs, labels = data
inputs, labels = inputs.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(inputs)
batch_loss = criterion(outputs, labels) #criterion=nn.CrossEntropyLoss()
_, train_pred = torch.max(outputs, 1) # get the index of the class with the highest probability
#torch.max 0代表每列的最大值,1代表每一行的最大值。
batch_loss.backward() #后向传播
optimizer.step() #参数更新
train_acc += (train_pred.cpu() == labels.cpu()).sum().item()
train_loss += batch_loss.item()
# validation
if len(val_set) > 0:
model.eval() # set the model to evaluation mode
with torch.no_grad():
for i, data in enumerate(val_loader):
inputs, labels = data
inputs, labels = inputs.to(device), labels.to(device)
outputs = model(inputs)
batch_loss = criterion(outputs, labels)
_, val_pred = torch.max(outputs, 1)
val_acc += (val_pred.cpu() == labels.cpu()).sum().item() # get the index of the class with the highest probability
val_loss += batch_loss.item()
print('[{:03d}/{:03d}] Train Acc: {:3.6f} Loss: {:3.6f} | Val Acc: {:3.6f} loss: {:3.6f}'.format(
epoch + 1, num_epoch, train_acc/len(train_set), train_loss/len(train_loader), val_acc/len(val_set), val_loss/len(val_loader)
))
# if the model improves, save a checkpoint at this epoch
if val_acc > best_acc:
best_acc = val_acc
torch.save(model.state_dict(), model_path)
print('saving model with acc {:.3f}'.format(best_acc/len(val_set)))
else:
print('[{:03d}/{:03d}] Train Acc: {:3.6f} Loss: {:3.6f}'.format(
epoch + 1, num_epoch, train_acc/len(train_set), train_loss/len(train_loader)
))
# if not validating, save the last epoch
if len(val_set) == 0:
torch.save(model.state_dict(), model_path)
print('saving model at last epoch')
测试以及保存过程
# create testing dataset
test_set = TIMITDataset(test, None)
test_loader = DataLoader(test_set, batch_size=BATCH_SIZE, shuffle=False)
# create model and load weights from checkpoint
model = Classifier().to(device)
model.load_state_dict(torch.load(model_path))
# Make prediction.
# In[ ]:
predict = []
model.eval() # set the model to evaluation mode
with torch.no_grad():
for i, data in enumerate(test_loader):
inputs = data
inputs = inputs.to(device)
outputs = model(inputs)
_, test_pred = torch.max(outputs, 1) # get the index of the class with the highest probability
for y in test_pred.cpu().numpy():
predict.append(y)
# Write prediction to a CSV file.
#
# After finish running this block, download the file `prediction.csv` from the files section on the left-hand side and submit it to Kaggle.
# In[ ]:
with open('prediction.csv', 'w') as f:
f.write('Id,Class\n')
for i, y in enumerate(predict):
f.write('{},{}\n'.format(i, y))
Adaptive learning rate
在这里我想总结一下李宏毅老师的前几课类神经网络训练不起来怎么办
optimizer的问题
相关课程链接放到这里New Optimizers for Deep Learning
首先,方法有哪些?
1. SGD
2. SGD with momentum(SGDM)
3. Adagrad
4. RMSProp
5. Adam
Some Notations:
SGD(1847)
SGDM(1986)
Adagrad(2011)
RMSProp(2013)
改进版的Adagrad
Adam(2015)
Adam | SGD |
---|---|
fast training ,large generalization gap,unstable | stable,little generalization gap,better convergence |
SWATS(2017)
AMSGrad(2018)
AdaBound(2017)
Cyclical LR(2017)
RAdam(2020)
右下角的是warm-up的learning rate的曲线
Lookahead
L2 regularization
Someting helps optimization
Batch的影响
shuffle 等于true是指每一个epoch分的batch都不一样。
small batch | large batch |
---|---|
long time for cooldown but powerful | short time for cooldown but noisy |
1. Large batch size does not require longer time to compute gradient
60次Batch_Size为1000跑完一个epoch的时间较短。
2.Smaller batch size has better performance
3.Small batch is better on testing data
这个图给出了一种解释,为什么小的batchsize会对test有帮助。首先,我们假设test的loss就是train的loss平移一段。如果train上的loss找到的是一个类似Flat Minima的的点,那么在test上也没有太大的影响。可是如果train上的loss找到的是一个sharp Minima。可以发现,此时在test上取到的是一个非常差的点。我们说,large batch一般来说很容易让train上的loss找到sharp Minima这种点,而small batch很容易让loss找到flat minma这种点,所以small batch对于test来说是有好处的。不过这只能作为一种解释,不能作为结论。
4.So,Batch size is a hyperparameter you have to decide
Loss 的影响(为什么要用cross-entropy)
classification 相比于 regression 加入softmax
Minimizing cross-entropy is equivalent to maximizing likelihood
pytorch的cross—entropy内嵌了softmax
criterion = nn.CrossEntropyLoss()
如果初始点在右上角,MSE的loss可能降不下来,因为MSE在右上角的gradient很小。