分别使用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. 在进行多个模型训练、性能对比的时候,要注意我们将相同的训练数据喂给多个模型,而不是打乱一次数据喂一个模型。考试的时候每个人拿到的是不同的卷子,那样成绩有可比性吗?
相关推荐
Elastic 中国社区官方博客23 分钟前
使用 Elastic AI Assistant for Search 和 Azure OpenAI 实现从 0 到 60 的转变
大数据·人工智能·elasticsearch·microsoft·搜索引擎·ai·azure
江_小_白1 小时前
自动驾驶之激光雷达
人工智能·机器学习·自动驾驶
yusaisai大鱼3 小时前
TensorFlow如何调用GPU?
人工智能·tensorflow
珠海新立电子科技有限公司5 小时前
FPC柔性线路板与智能生活的融合
人工智能·生活·制造
IT古董5 小时前
【机器学习】机器学习中用到的高等数学知识-8. 图论 (Graph Theory)
人工智能·机器学习·图论
曼城周杰伦6 小时前
自然语言处理:第六十三章 阿里Qwen2 & 2.5系列
人工智能·阿里云·语言模型·自然语言处理·chatgpt·nlp·gpt-3
余炜yw6 小时前
【LSTM实战】跨越千年,赋诗成文:用LSTM重现唐诗的韵律与情感
人工智能·rnn·深度学习
莫叫石榴姐7 小时前
数据科学与SQL:组距分组分析 | 区间分布问题
大数据·人工智能·sql·深度学习·算法·机器学习·数据挖掘
96777 小时前
对抗样本存在的原因
深度学习
如若1237 小时前
利用 `OpenCV` 和 `Matplotlib` 库进行图像读取、颜色空间转换、掩膜创建、颜色替换
人工智能·opencv·matplotlib