这篇文章主要是看看别人的代码怎么写,自己目前对于分类问题还是不太会(菜哭了)
数据集
kaggle
数据地址:
https://www.kaggle.com/competitions/ml2021spring-hw2/data
代码地址:
https://www.kaggle.com/code/lizupeng/notebook4bf3cf8e90
代码
# This Python 3 environment comes with many helpful analytics libraries installed
# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python
# For example, here's several helpful packages to load
import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
# Input data files are available in the read-only "../input/" directory
# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory
import os
for dirname, _, filenames in os.walk('/kaggle/input'):
for filename in filenames:
print(os.path.join(dirname, filename))
# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using "Save & Run All"
# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session
“”“kaggle经典开头代码,没什么好说的”“”
———————————————分割线——————————————
print('Loading data ...')
data_root = '/kaggle/input/ml2021spring-hw2/timit_11/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')
"""
这里采用的np.load,不是pd
我们的文件形式.npy
ChatGPT:
np.load() 是 NumPy 库中用于加载 NumPy 保存的数组数据的函数。它允许你从磁盘上的 .npy 文件中加载数据并将其转换为 NumPy 数组,以便在 Python 中使用。
numpy.load(file, mmap_mode=None, allow_pickle=True, fix_imports=True, encoding='ASCII')
参数说明:
file: 要加载的 .npy 文件的路径。
mmap_mode:指定文件的内存映射模式。默认为 None,表示不使用内存映射。其他选项包括 'r'(只读模式),'r+'(读写模式)等。
allow_pickle:布尔值,指示是否允许加载包含 Python 对象的 .npy 文件。默认为 True。
fix_imports:布尔值,指示是否自动修复 Python 2 和 Python 3 之间的 pickle 文件中的 import 语句。默认为 True。
encoding:指定文件的编码。默认为 'ASCII'。
"""
print('Size of training data: {}'.format(train.shape))
print('Size of training data: {}'.format(test.shape))
Loading data ...
Size of training data: (1229932, 429)
Size of training data: (451552, 429)
# 定义数据集,经典的三个函数
import torch
from torch.utils.data import Dataset
# 定义数据集
class TIMITDataset(Dataset):
# 测试节和训练集都需要用这个类
def __init__(self, X, y=None):
self.data = torch.from_numpy(X).float()
# y=None 对应的是测试集
if y is not None:
y = y.astype(np.int32)
# 多分类任务,label是一个long
self.label = torch.LongTensor(y)
else:
self.label = None
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)
# VAL_RATIO为0.2 就是把train、 train_label数据集的80%用于训练,20%用于交叉验证
VAL_RATIO = 0.2
# percent 前80%的数据index
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))
Size of training set: (983945, 429)
Size of validation set: (245987, 429)
# 管理一下内存
import gc
del train, train_label, train_x, train_y, val_x, val_y
gc.collect()
# 检查此刻内存占用情况(值越小越好,一开始是120,删掉上述这些变量后减小为50)
"""定义网络"""
import torch
import torch.nn as nn
class Classifier(nn.Module):
def __init__(self):
super(Classifier, self).__init__()
# 其实这里可以看出,layer1、layer2 、、、、、act_fn、、、、这些都是这个类的全局变量
# 你定义这些层,如果可以是可以重复使用的
# 这和nn.Sequential() 不太一样,
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
# cpu or gpu
def get_device():
return 'cuda' if torch.cuda.is_available() else 'cpu'
# fix random seed 修改随机数,保证每一次训练结果一样,这段代码不需要背会,用的时候直接cv即可
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)
device = get_device()
# training
num_epoch = 20
learning_rate = 0.0001
# 设置checkpoint的存放位置,其实就是存放最优模型的位置,ckpt是文件类型、写pth也没错
model_path = './model.ckpt'
model = Classifier().to(device)
criterion = nn.CrossEntropyLoss()
# pytorch中如果采用交叉验证的损失函数,那么一定是分类任务,这时候softmax是包含在crossEntropyLoss函数中的,不需要额外调用
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
"""接下来是重点"""
# train
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
model.train()
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)
# 取可能性最大的一个,这里的train_pred是用来计算acc精准度的
_, train_pred = torch.max(outputs, 1)
batch_loss.backward()
optimizer.step()
# train_pred
train_acc += (train_pred.cpu() == labels.cpu()).sum().item()# 此时的train_acc是在计算预测正确的语言的个数,如果当前语言预测正确,则train_acc+1
train_loss += batch_loss.item()# train_loss用于计算总的训练的loss
if len(val_set) > 0:
model.eval()
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()
val_loss += batch_loss.item()
# 打印每一轮epoch的训练精度、训练loss、验证精度、验证loss
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)
))
# 这样一来最后保存的模型文件只有一个,且保存的这一个模型文件是精度最高的那一次epoch所训练的模型文件
if val_acc > best_acc:
beat_acc = val_acc
torch.save(model.state_dict(), model_path)
print('saving model with acc {:.3f}'.format(best_acc/len(val_set)))
else:# 这个else是给验证集为0时准备的,当我们设置超参VAL_RATIO为0时,验证集为0,就要执行这个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)
))
# 这一步也是为了防止没有验证集所写的,
# 如果验证集为0,则不会有最优模型被保存,为防止出bug,设置:当没有验证集时,选取最后一次epoch训练的模型为最优模型并保存
if len(val_set) == 0:
torch.save(model.state_dict(), model_path)
print('saving model at last epoch')
"""
我的训练结果:
[001/020] Train Acc: 0.594383 Loss: 1.330665 | Val Acc: 0.628639 loss: 1.211098
saving model with acc 0.000
[002/020] Train Acc: 0.644506 Loss: 1.154064 | Val Acc: 0.660421 loss: 1.101215
saving model with acc 0.000
[003/020] Train Acc: 0.672217 Loss: 1.052246 | Val Acc: 0.676300 loss: 1.038718
saving model with acc 0.000
[004/020] Train Acc: 0.691347 Loss: 0.983103 | Val Acc: 0.685154 loss: 1.001852
saving model with acc 0.000
[005/020] Train Acc: 0.705615 Loss: 0.931955 | Val Acc: 0.689301 loss: 0.984177
saving model with acc 0.000
[006/020] Train Acc: 0.716344 Loss: 0.891687 | Val Acc: 0.694516 loss: 0.964627
saving model with acc 0.000
[007/020] Train Acc: 0.725881 Loss: 0.857907 | Val Acc: 0.697720 loss: 0.951889
saving model with acc 0.000
[008/020] Train Acc: 0.733718 Loss: 0.829495 | Val Acc: 0.696687 loss: 0.949866
saving model with acc 0.000
[009/020] Train Acc: 0.741151 Loss: 0.803701 | Val Acc: 0.699374 loss: 0.944832
saving model with acc 0.000
[010/020] Train Acc: 0.748050 Loss: 0.781106 | Val Acc: 0.697773 loss: 0.946494
saving model with acc 0.000
[011/020] Train Acc: 0.753793 Loss: 0.760380 | Val Acc: 0.702830 loss: 0.938236
saving model with acc 0.000
[012/020] Train Acc: 0.759404 Loss: 0.741234 | Val Acc: 0.700452 loss: 0.945627
saving model with acc 0.000
[013/020] Train Acc: 0.764573 Loss: 0.723574 | Val Acc: 0.702159 loss: 0.942118
saving model with acc 0.000
[014/020] Train Acc: 0.769471 Loss: 0.707325 | Val Acc: 0.704427 loss: 0.936154
saving model with acc 0.000
[015/020] Train Acc: 0.773687 Loss: 0.691314 | Val Acc: 0.701736 loss: 0.945713
saving model with acc 0.000
[016/020] Train Acc: 0.778676 Loss: 0.676633 | Val Acc: 0.701586 loss: 0.953081
saving model with acc 0.000
[017/020] Train Acc: 0.783107 Loss: 0.662425 | Val Acc: 0.699655 loss: 0.963290
saving model with acc 0.000
[018/020] Train Acc: 0.786394 Loss: 0.649180 | Val Acc: 0.700078 loss: 0.957681
saving model with acc 0.000
[019/020] Train Acc: 0.790645 Loss: 0.636623 | Val Acc: 0.699732 loss: 0.964269
saving model with acc 0.000
[020/020] Train Acc: 0.794713 Loss: 0.623686 | Val Acc: 0.694821 loss: 0.981714
saving model with acc 0.000
精准度不是很高,用的cpu训练,耗时将近一个半小时(哭)
"""
# 个人的代码就运行到这里,下面的代码没有亲自运行,大家自行看看把,主要是测试集的处理
# ---------------------------分割线-------------------------------------------------
# create testing dataset
test_set = TIMITDataset(test, None)
test_loader = DataLoader(test_set, batch_size=BATCH_SIZE, shuffle=False)
# del test_set # 删除不再需要的大型变量,防止内存爆掉
# create model and load weights from checkpoint 加载保存的模型
model = Classifier().to(device)
model.load_state_dict(torch.load(model_path))
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(): # 这里之所以使用for是因为,预测值不止一个(因为模型是按照batch加载的,训练也是每batch个音频一起训练,所以得到的应该是batch个预测值)
predict.append(y)
with open('prediction.csv', 'w') as f:
f.write('Id,Class\n')
for i, y in enumerate(predict):
f.write('{},{}\n'.format(i, y))