分别使用MLP,CNN和Attention对Kaggle上的LOL比赛数据进行预测胜负

写在前面

这篇博客只是记录一下在训练神经网络中的一些问题和熟悉一下流程,毕竟好久没有训练一个模型了。课程正好需要我们去进行训练,我就选择了三个比较经典的模型进行预测。过程分为:处理数据,划分训练集和预测集,定义模型和优化器,生成混淆矩阵。

整个部分比较简单,使用了GPU进行训练,主要是熟悉训练网络的流程和框架。

处理数据

首先是读入数据,使用了pandas读数据

python 复制代码
csv_data = './data/high_diamond_ranked_10min.csv' # 数据路径
data_df = pd.read_csv(csv_data, sep=',') # 读入csv文件为pandas的DataFrame
data_df = data_df.drop(columns='gameId') # 舍去对局标号列

之后对数据中的特征进行处理,主要是舍弃了一些不太重要的特征

python 复制代码
drop_features = ['blueGoldDiff', 'redGoldDiff', 
                 'blueExperienceDiff', 'redExperienceDiff', 
                 'blueCSPerMin', 'redCSPerMin', 
                 'blueGoldPerMin', 'redGoldPerMin'] # 需要舍去的特征列
df = data_df.drop(columns=drop_features) # 舍去特征列
info_names = [c[3:] for c in df.columns if c.startswith('red')] # 取出要作差值的特征名字(除去red前缀)
for info in info_names: # 对于每个特征名字
    df['br' + info] = df['blue' + info] - df['red' + info] # 构造一个新的特征,由蓝色特征减去红色特征,前缀为br
# 其中FirstBlood为首次击杀最多有一只队伍能获得,brFirstBlood=1为蓝,0为没有产生,-1为红
df = df.drop(columns=['blueFirstBlood', 'redFirstBlood']) # 原有的FirstBlood可删除

之后将特征数值归一化,这里采用了减平均值除以标准差的形式。

python 复制代码
discrete_df = df.values.copy()
row,col = discrete_df.shape
for i in range(1,col):
    avg = np.mean(discrete_df[:,i])
    std = np.std(discrete_df[:,i])
    discrete_df[:,i] = (discrete_df[:,i]-avg)/std

划分训练集和测试集

这里要注意将输入和输出的type转为float32(初始是float64,不修改会和模型中的参数发生不匹配),并且需要将DataFrame转为array(这里博主踩坑了很久,记得修改)。另外all_x的第一列就是标签,需要删除第一列的元素。

python 复制代码
all_y = df['blueWins'].values.astype(np.float32) # 所有标签数据
all_x = discrete_df.astype(np.float32)[:,1:] # 所有原始特征值,pandas的DataFrame.values取出为numpy的array矩阵

# 划分训练集和测试集
x_train, x_test, y_train, y_test = train_test_split(all_x, all_y, test_size=0.2, random_state=RANDOM_SEED)

接下来是定义超参数。为了训练方便,我们都使用TensorDataset和DataLoader进行数据读取,同时还可以使用cuda加速。(使用cuda加速需要cuda版本的输入和输出,以及模型)

python 复制代码
input_size = 43  # 输入特征的维度  
hidden_size = 64  # 隐藏层的神经元数量  
num_classes = 2  # 二分类问题,所以输出层有2个神经元(对应于两个类别)  
num_epochs = 100  # 训练周期数  
batch_size = 64  # 批处理大小  
learning_rate = 0.013  # 学习率

train_dataset = TensorDataset(torch.tensor(x_train), torch.tensor(y_train))  
train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)

test_dataset = TensorDataset(torch.tensor(x_test), torch.tensor(y_test))  
test_dataloader = DataLoader(test_dataset, batch_size=batch_size, shuffle=True)

定义模型

MLP

python 复制代码
class SimpleNN(nn.Module):  
    def __init__(self, input_size, hidden_size, num_classes):  
        super(SimpleNN, self).__init__()  
        self.fc1 = nn.Linear(input_size, hidden_size)  # 输入层到隐藏层的线性变换  
        self.relu = nn.ReLU()  # 非线性激活函数ReLU  
        self.fc2 = nn.Linear(hidden_size, 16)  # 隐藏层到输出层的线性变换  
        self.fc3 = nn.Linear(16,num_classes)

      
    def forward(self, x):  

        out = self.fc1(x)  
        out = self.relu(out)  
        out = self.fc2(out)  
        out = self.relu(out)  
        out = self.fc3(out)
        return out
    
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")  # 检查是否有可用的GPU,并设置设备为GPU或CPU 
model = SimpleNN(input_size,hidden_size,num_classes) 
model = model.to(device)  # 将模型移动到选定的设备上(GPU或CPU)

Attention

python 复制代码
class AttentionModel(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(AttentionModel, self).__init__()
        
        # 定义注意力权重计算
        self.attention = nn.Sequential(
            nn.Linear(input_size, hidden_size),
            nn.Tanh(),
            nn.Linear(hidden_size, 1),
            nn.Softmax(dim=1)
        )
        
        # 定义分类器
        self.classifier = nn.Sequential(
            nn.Linear(input_size, output_size),
            nn.Sigmoid()  # 适用于二分类任务
        )

    def forward(self, x):
        
        # x:[32,43]
        # 计算注意力权重
        attention_weights = self.attention(x)
        # attendtion:[32,1]
        # 使用注意力权重加权输入特征
        attended_input = x * attention_weights
        # attended:[32*43]
        # 使用分类器进行预测
        output = self.classifier(attended_input)
        return output
    
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")  # 检查是否有可用的GPU,并设置设备为GPU或CPU 
model = AttentionModel(input_size,hidden_size,num_classes) 
model = model.to(device)  # 将模型移动到选定的设备上(GPU或CPU)

CNN

python 复制代码
class CNN(nn.Module):  
    def __init__(self,input_size,hidden_size,num_classes):  
        super(CNN, self).__init__()  
        self.conv1 = nn.Conv1d(1, 32, kernel_size=3, stride=1, padding=1)  
        self.conv2 = nn.Conv1d(32, 64, kernel_size=3, stride=1, padding=1)  
        self.conv3 = nn.Conv1d(64, 128, kernel_size=3, stride=1, padding=1)  
        self.fc1 = nn.Linear(5504, 256)  # 根据实际特征大小调整  
        self.fc2 = nn.Linear(256, num_classes)  
          
    def forward(self, x):  
        x = x.reshape(-1,1,43)
        x = F.relu(self.conv1(x))  
        x = F.relu(self.conv2(x))  
        x = F.relu(self.conv3(x))  
        x = x.view(-1, 5504)  # 展平操作,根据实际特征大小调整  
        x = F.relu(self.fc1(x))  
        x = self.fc2(x)  
        return x

device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu")  # 检查是否有可用的GPU,并设置设备为GPU或CPU 
model = CNN(input_size,hidden_size,num_classes) 
model = model.to(device)  # 将模型移动到选定的设备上(GPU或CPU)

训练和测试

先定义一下优化器和损失函数

python 复制代码
criterion = nn.CrossEntropyLoss()  # 二分类问题使用交叉熵损失函数  
optimizer = optim.SGD(model.parameters(),momentum=0.9,lr=learning_rate)  # 使用随机梯度下降优化器

然后进行训练,输出训练loss和测试loss。注意测试数据不要用来进行参数优化

python 复制代码
for epoch in range(num_epochs):  
    train_loss = 0.0
    for i, (features, labels) in enumerate(train_dataloader):  
        features = features.to(device)  # 将数据移动到GPU上(如果有可用)  
        labels = labels.to(device)  # 将标签移动到GPU上(如果有可用)  
        
        outputs = model(features)  # 前向传播,计算输出值  
        loss = criterion(outputs, labels.long())  # 计算损失值 
        train_loss += loss.item()
        optimizer.zero_grad()  # 清空梯度缓存  
        loss.backward()  # 反向传播,计算梯度值并更新权重  
        optimizer.step()  # 使用优化器更新权重和偏置项  
    test_loss = 0.0
    for j, (features, labels) in enumerate(test_dataloader):  
        features = features.to(device)  # 将数据移动到GPU上(如果有可用)  
        labels = labels.to(device)  # 将标签移动到GPU上(如果有可用)
        outputs = model(features)
        loss = criterion(outputs, labels.long())  # 计算损失值 
        test_loss += loss.item()
    print(f"Epoch {epoch+1}/{num_epochs}, Train Loss: {train_loss/i}, Test Loss: {test_loss/j}")  # 打印当前训练周期的损失值

生成混淆矩阵

这里我使用了数据集和测试集一起生成混淆矩阵。但我觉得模型在训练的时候可能会出现过拟合情况,所以这样得到的混淆矩阵可能会过于准确,仅使用测试集可能效果会更真实。

python 复制代码
from sklearn.metrics import confusion_matrix    # 生成混淆矩阵函数
import matplotlib.pyplot as plt 


pred_output = model(torch.tensor(all_x).to(device)).cpu().detach()
y_true = []
y_pred = []
for i,pred_label in enumerate(pred_output):
    y_true.append(all_y[i])
    y_pred.append(np.argmax(pred_label))


def draw_confusion_matrix(label_true, label_pred, label_name, title="Confusion Matrix", pdf_save_path=None, dpi=300):
    cm = confusion_matrix(y_true=label_true, y_pred=label_pred, normalize='true')

    plt.imshow(cm, cmap='Blues')
    plt.title(title)
    plt.xlabel("Predict label")
    plt.ylabel("Truth label")
    plt.yticks(range(label_name.__len__()), label_name)
    plt.xticks(range(label_name.__len__()), label_name, rotation=45)

    plt.tight_layout()

    plt.colorbar()

    for i in range(label_name.__len__()):
        for j in range(label_name.__len__()):
            color = (1, 1, 1) if i == j else (0, 0, 0)  # 对角线字体白色,其他黑色
            value = float(format('%.2f' % cm[j, i]))
            plt.text(i, j, value, verticalalignment='center', horizontalalignment='center', color=color)

    # plt.show()
    if not pdf_save_path is None:
        plt.savefig(pdf_save_path, bbox_inches='tight', dpi=300)
    plt.close()

draw_confusion_matrix(y_true,y_pred,[0,1],"Attention","./result/MLP+SDG.png")

注意问题

  1. 调试输入的维度,需要清楚模块如何对输入进行维度调整的
  2. SGD当加入momentum时容易过拟合,Adam则下降速度很慢,需要调大learning rate
  3. DataFrame转为array,使用obj.values
  4. tensor类型的to.(device),在GPU训练中不可缺少
  5. 采用什么数据生成混淆矩阵
  6. 在进行多个模型训练、性能对比的时候,要注意我们将相同的训练数据喂给多个模型,而不是打乱一次数据喂一个模型。考试的时候每个人拿到的是不同的卷子,那样成绩有可比性吗?
相关推荐
GISer_Jing2 分钟前
神经网络初学总结(一)
人工智能·深度学习·神经网络
szxinmai主板定制专家10 分钟前
【国产NI替代】基于A7 FPGA+AI的16振动(16bits)终端PCIE数据采集板卡
人工智能·fpga开发
千天夜30 分钟前
多源多点路径规划:基于启发式动态生成树算法的实现
算法·机器学习·动态规划
数据分析能量站1 小时前
神经网络-AlexNet
人工智能·深度学习·神经网络
Ven%1 小时前
如何修改pip全局缓存位置和全局安装包存放路径
人工智能·python·深度学习·缓存·自然语言处理·pip
szxinmai主板定制专家1 小时前
【NI国产替代】基于国产FPGA+全志T3的全国产16振动+2转速(24bits)高精度终端采集板卡
人工智能·fpga开发
YangJZ_ByteMaster1 小时前
EndtoEnd Object Detection with Transformers
人工智能·深度学习·目标检测·计算机视觉
Anlici1 小时前
模型训练与数据分析
人工智能·机器学习
余~~185381628002 小时前
NFC 碰一碰发视频源码搭建技术详解,支持OEM
开发语言·人工智能·python·音视频
唔皇万睡万万睡2 小时前
五子棋小游戏设计(Matlab)
人工智能·matlab·游戏程序