比赛信息
Otto Group Product Classification
https://www.kaggle.com/c/otto-group-product-classification-challenge
工具:PyTorch 1.6.0
信息:
- 93特征值
- 分类数为9类
这里假设特征之间没有关联,是个简单的仅分类问题,直接使用全连接层完成神经网络的搭建
面向PyTorch编程法则
- 准备数据集
- 数据集清洗
- 训练集顺序打乱
- 搭建神经网络
- 线性Linear层
- 一般用于分类、预测
- CNN卷积神经网络
- 适用于大于等于二维数据
- 单/双向RNN循环神经网络
- 一般适用于一维数据+特征项之间有联系
- 多用于语义分析,预测
- 还有GRU、LSTM
- 线性Linear层
- 选取损失函数
- 二分交叉熵
- 交叉熵、二分交叉熵…
- …
- 选取优化器
- Adam、SGD…
- 搭建模型
- 训练模型
- 评估模型
正片开始
此处使用Google Colab的GPU进行运算
导包、定义GPU是否开启
1
2
3
4
5
6
7
8
9
import torch
import torchvision
import pandas as pd
import numpy as np
import os
from torch.utils.data import Dataset,DataLoader
# GPU配置
USE_GPU = True
导入数据、数据处理
- 此处要把Class_1-Class-9转换为0-8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
root_dir = "/content/drive/My Drive/Colab Notebooks/otto-group-product-classification-challenge"
test_path = "test.csv"
train_path = "train.csv"
test_csv = os.path.join(root_dir,test_path)
train_csv = os.path.join(root_dir,train_path)
# 转换为数值型数据
train_data = pd.read_csv(train_csv)
def convertClass(s):
if type(s) == int:
return s
s = s.replace("Class_","")
return int(s)-1
train_data["target"] = train_data["target"].map(convertClass)
test_data = pd.read_csv(test_csv)
#test_data["target"] = train_data["target"].map(convertClass)
# 训练的时候不需要id,此处drop掉
train_data.drop("id",axis=1,inplace=True)
实现Dataset类
- 实现
__init__
、__getitem(self,index)
、__len__(self)(可选)
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
class MyDataset(Dataset):
def __init__(self,data):
data
self.x_data = torch.from_numpy(np.array(data,dtype='float32')[:,:-1])
self.y_data = torch.from_numpy(np.array(data,dtype='float32')[:,-1])
self.length = data.shape[0]
def __getitem__(self,index):
return (self.x_data[index],self.y_data[index])
def __len__(self):
return self.length
class TestDataset(Dataset):
def __init__(self,data):
self.x_data = torch.from_numpy(np.array(data,dtype='float32'))
self.length = data.shape[0]
def __getitem__(self,index):
return (self.x_data[index])
def __len__(self):
return self.length
train_dataset = MyDataset(train_data)
test_dataset = TestDataset(test_data)
模型定义
-
使用Linear进行全连接
-
使用ReLU6进行激活(ReLU6比较新,先试试)
-
若启用了GPU,则
model = model.cuda()
-
若使用的是交叉熵损失,则最后一层不需要激活
-
CrossEntropyLoss中会使用Softmax对最后一层的输出进行激活
-
PS:激活是为了更好的拟合非线性输出,线性层+激活->更好拟合非线性输出
-
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
# 全连接模型
class TrainModel(torch.nn.Module):
def __init__(self):
super(TrainModel,self).__init__()
# layers
self.linear1 = torch.nn.Linear(93,48)
self.linear2 = torch.nn.Linear(48,24)
self.linear3 = torch.nn.Linear(24,12)
self.linear4 = torch.nn.Linear(12,9)
self.relu = torch.nn.ReLU6()
def forward(self,x):
x = self.relu(self.linear1(x))
x = self.relu(self.linear2(x))
x = self.relu(self.linear3(x))
# 由于是交叉熵损失,最后一步不激活
return self.linear4(x)
model = TrainModel()
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters())
# 若开启了GPU,则导入cuda
if USE_GPU:
model = model.cuda()
训练模型
- 训练之前,先清空优化器的梯度
- loss不要每次都输出,可以在一个epoch输出一次,或者每隔300次输出一次
- 编写逻辑顺序
- 清空梯度
- 前向传播xs
- 反向传播loss
- 优化器调整模型参数
- 循环…
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
for epoch in range(10000):
print("training (epoch %d)"%epoch)
total_loss = 0
cnt = 0
for index,(xs,ys) in enumerate(train_loader,0):
if USE_GPU:
xs = torch.tensor(xs)
ys = torch.tensor(ys)
xs = xs.to(device)
ys = ys.to(device)
optimizer.zero_grad()
y_pred = model(xs)
loss = criterion(y_pred,ys.long())
total_loss += loss.item()
loss.backward()
optimizer.step()
cnt += 1
print("epoch %d, loss: %f"%(epoch,total_loss/cnt))
写完之后
开始炼丹!!!
- 可以使用Tensorboard或者Matplotlib对loss进行可视化,可以看看loss下降趋势
- 有测试集的话可以对其进行测试,查看准确率
- 记得使用
torch.no_grad()
取消梯度
- 记得使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def test():
correct = 0
total = 0
with torch.no_grad():
print("testing...")
for index,(x) in enumerate(test_loader,0):
if USE_GPU:
x = x.cuda()
output = model(x[:,1:])
output = torch.max(output,1)[1]
print("x",x[:,0])
print("pred",output + 1)
# 官方没有测试集的结果
此处省略
#print("accu: %d %%"%(100*correct/total))