这份代码是数据处理模块,它的核心任务非常明确:把硬盘上的 jiudian.txt 文本文件读取出来,经过清洗和分割,最终打包成 PyTorch 模型能直接“消化”的 DataLoader。

下面我为您逐行详细解释这段代码:

1. 导入库

python

编辑

from torch.utils.data import DataLoader, Dataset
from sklearn.model_selection import train_test_split
import torch
  • from torch.utils.data import DataLoader, Dataset:
    • Dataset: 这是 PyTorch 的数据集抽象类。我们需要继承它来创建一个自定义的数据集类,告诉 PyTorch 怎么读取我们的数据。
    • DataLoader: 这是一个迭代器。它负责把 Dataset 里的数据打包成一个个小批次(Batch),并支持多线程加载和数据打乱(Shuffle)。
  • from sklearn.model_selection import train_test_split:
    • 这是一个非常实用的工具函数。它可以自动把数据切分成训练集和验证集。
  • import torch:
    • 导入 PyTorch 库,主要用于将标签数据转换为张量(Tensor)。

2. 读取数据函数

python

编辑

def read_file(path):
    data = []
    label = []
  • def read_file(path):: 定义一个函数,参数 path 是数据文件的路径。
  • data = []: 初始化一个空列表,用来存放文本内容(例如:“这家酒店真不错”)。
  • label = []: 初始化一个空列表,用来存放标签(例如:“1”代表好评)。

python

编辑

    with open(path, "r", encoding="utf-8") as f:
        for i, line in enumerate(f):
  • with open(...): 以只读模式("r")打开文件,并指定编码为 utf-8(防止中文乱码)。with 语句会自动在读取结束后关闭文件。
  • for i, line in enumerate(f):: 逐行读取文件。
    • i: 行号索引(从 0 开始)。
    • line: 当前行的文本内容。

python

编辑

            if i == 0:
                continue
  • 跳过第一行。通常数据文件的第一行是表头(例如“label,text”),不是实际数据,所以跳过。

python

编辑

            if i > 200 and i< 7500:
                continue
  • 数据筛选逻辑。这行代码的意思是:如果行号在 200 到 7500 之间,就跳过不读。
  • 推测用途:这通常是为了快速调试代码。作者可能只想用前 200 条数据测试程序能不能跑通,或者只想用 7500 条之后的数据。在全量训练时,通常会注释掉这一行。

python

编辑

            line = line.strip("\n")
            line = line.split(",", 1)
  • line.strip("\n"): 去掉行末尾的换行符。
  • line.split(",", 1): 按照逗号 , 分割字符串。
    • 1: 表示只分割一次。
    • 为什么要这样做? 假设文本内容是 "1,这,家,酒,店,真,好"。如果不限制分割次数,会被切成很多段。限制为 1 次,就能保证切分成 [标签, 剩下的所有文本] 两部分。

python

编辑

            data.append(line[1])
            label.append(line[0])
  • data.append(line[1]): 把分割后的第二部分(文本内容)加入 data 列表。
  • label.append(line[0]): 把分割后的第一部分(标签)加入 label 列表。

python

编辑

    print("读了%d的数据"%len(data))
    return data, label
  • 打印读取了多少条数据,并返回两个列表。

3. 自定义数据集类

python

编辑

class jdDataset(Dataset):
    def __init__(self, data, label):
        self.X = data
        self.Y = torch.LongTensor([int(i) for i in label])
  • class jdDataset(Dataset):: 定义一个名为 jdDataset 的类,继承自 PyTorch 的 Dataset。
  • def __init__(self, data, label):: 初始化函数。
    • self.X = data: 保存文本数据。
    • self.Y = ...: 处理标签数据。
      • [int(i) for i in label]: 把标签从字符串(如 "1")转换成整数(如 1)。
      • torch.LongTensor(...): 将整数列表转换成 PyTorch 的长整型张量。
      • 注意:分类任务的标签(Target)必须是 Long 类型,因为交叉熵损失函数需要它来索引类别。

python

编辑

    def __getitem__(self, item):
        return self.X[item], self.Y[item]
  • def __getitem__(self, item):: 这是一个魔术方法。当你通过索引访问数据(例如 dataset[5])时,这个方法会被调用。
  • return ...: 返回第 item 条数据的文本和对应的标签。

python

编辑

    def __len__(self):
        return len(self.Y)
  • def __len__(self):: 返回数据集的总大小。DataLoader 需要知道总共有多少数据才能确定循环多少次。

4. 获取数据加载器

python

编辑

def get_data_loader(path, batchsize, val_size=0.2):
    data, label = read_file(path)
  • def get_data_loader(...): 定义主函数。
    • val_size=0.2: 默认验证集比例是 20%。
  • data, label = read_file(path): 调用上面的函数,读取原始数据。

python

编辑

    train_x, val_x, train_y, val_y = train_test_split(data, label, test_size=val_size, shuffle=True, stratify=label)
  • train_test_split(...): 核心分割函数。
    • data, label: 输入的文本和标签。
    • test_size=val_size: 指定验证集占 20%。
    • shuffle=True: 打乱数据。这很重要,防止模型学到数据的顺序(比如前100条全是好评)。
    • stratify=label: 分层采样。这意味着训练集和验证集中,“好评”和“差评”的比例会保持一致(例如都是 1:1),防止出现训练集全是好评,验证集全是差评的情况。

python

编辑

    train_set = jdDataset(train_x, train_y)
    val_set = jdDataset(val_x, val_y)
  • 将分割好的训练数据和验证数据,分别封装成我们刚才定义的 jdDataset 对象。

python

编辑

    train_loader = DataLoader(train_set, batchsize, shuffle=True)
    val_loader = DataLoader(val_set, batchsize, shuffle=True)
  • DataLoader(...): 生成数据加载器。
    • train_set: 传入数据集对象。
    • batchsize: 每个批次取多少条数据(例如 16)。
    • shuffle=True: 每个 epoch 开始时再次打乱数据顺序(增强模型泛化能力)。
    • 注意:通常验证集 val_loader 的 shuffle 可以设为 False,但这不影响结果,只是顺序不同而已。

python

编辑

    return train_loader, val_loader
  • 返回两个加载器,供主程序使用。

5. 测试代码

python

编辑

if __name__ == "__main__":
    get_data_loader("../jiudian.txt", 2)
  • 测试运行,指定文件路径和批次大小为 2。

这段代码是整个项目的核心“发动机”——训练与验证循环。它负责指挥模型如何利用数据进行学习,如何计算误差,以及如何根据验证集的表现来保存最好的模型。

下面我为您逐行详细解释:

1. 导入库

python

编辑

import torch
import time
import matplotlib.pyplot as plt
import numpy as np
from tqdm import tqdm
  • import torch: PyTorch 核心库。
  • import time: 用于计算训练每个 epoch 花费的时间。
  • import matplotlib.pyplot as plt: 绘图库,用于最后画出 Loss 和 Accuracy 的变化曲线。
  • import numpy as np: 用于数值计算(如计算准确率时的求和)。
  • from tqdm import tqdm: 一个进度条库。它可以在循环中显示一个动态的进度条,让你直观地看到训练进度。

2. 函数定义与参数解包

python

编辑

def train_val(para):
  • def train_val(para):: 定义训练函数,接收一个包含所有配置和对象的字典 para。

python

编辑

    model = para['model']
    train_loader =para['train_loader']
    val_loader = para['val_loader']
    scheduler = para['scheduler']
    optimizer = para['optimizer']
    loss = para['loss']
    epoch = para['epoch']
    device = para['device']
    save_path = para['save_path']
    max_acc = para['max_acc']
    val_epoch = para['val_epoch']
  • 这一大段代码是从字典 para 中提取出之前定义好的变量,方便后续直接使用。
    • model: 模型本体。
    • train_loader / val_loader: 数据加载器。
    • scheduler: 学习率调度器。
    • optimizer: 优化器。
    • loss: 损失函数。
    • max_acc: 用于记录目前为止最好的验证集准确率,用于筛选最佳模型。
    • val_epoch: 验证频率(每隔几个 epoch 验证一次)。

3. 初始化记录列表

python

编辑

    plt_train_loss = []
    plt_train_acc = []
    plt_val_loss = []
    plt_val_acc = []
    val_rel = []
  • 初始化 5 个空列表,用于在训练过程中记录数据,以便最后画图。
    • plt_train_loss / plt_train_acc: 记录每个 epoch 的训练集 Loss 和准确率。
    • plt_val_loss / plt_val_acc: 记录每个 epoch 的验证集 Loss 和准确率。
    • val_rel: 用于保存验证集的预测结果(虽然代码后面没怎么用到这个变量,可能是为了后续分析预留的)。

4. 训练循环 (Epoch Loop)

python

编辑

    for i in range(epoch):
        start_time = time.time()
        model.train()
        train_loss = 0.0
        train_acc = 0.0
        val_acc = 0.0
        val_loss = 0.0
  • for i in range(epoch):: 开始循环,i 代表当前的 epoch 轮数(从 0 到 4)。
  • start_time = time.time(): 记录当前时间,用于计算这一轮花了多久。
  • model.train(): 关键! 将模型设置为训练模式。这会开启 Dropout 和 BatchNorm 的训练行为。
  • train_loss = 0.0 ...: 初始化当前 epoch 的累计 Loss 和准确率计数器。

5. 训练阶段 (Training Phase)

python

编辑

        for batch in tqdm(train_loader):
            model.zero_grad()
            text, labels = batch[0], batch[1].to(device)
            pred = model(text)
            bat_loss = loss(pred, labels)
            bat_loss.backward()
            optimizer.step()
            scheduler.step()              #scheduler     调整学习率
            optimizer.zero_grad()
            torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)       #梯度裁切
            train_loss += bat_loss.item()    #.detach 表示去掉梯度
            train_acc += np.sum(np.argmax(pred.cpu().data.numpy(),axis=1)== labels.cpu().numpy())

这是最核心的训练步骤,逐行解释:

  • for batch in tqdm(train_loader):: 遍历训练数据。tqdm 会自动显示进度条。
  • model.zero_grad(): 清空梯度。PyTorch 默认会累加梯度,所以每次迭代前必须手动清空。
  • text, labels = ...: 从 batch 中取出文本和标签,并将标签移动到 GPU (device) 上。
  • pred = model(text): 前向传播。将文本输入模型,得到预测结果。
  • bat_loss = loss(pred, labels): 计算损失。比较预测结果 pred 和真实标签 labels。
  • bat_loss.backward(): 反向传播。计算梯度(即 Loss 对每个参数的导数)。
  • optimizer.step(): 更新参数。优化器根据梯度更新模型的权重。
  • scheduler.step(): 更新学习率。根据调度器策略调整学习率。
  • optimizer.zero_grad(): 再次清空梯度(这是一种防御性写法,虽然上面已经清过一次了)。
  • torch.nn.utils.clip_grad_norm_(...): 梯度裁剪。防止梯度爆炸(即梯度数值过大导致模型崩溃),将梯度限制在 1.0 以内。
  • train_loss += bat_loss.item(): 累加当前的 Loss 值。.item() 是取出 Tensor 中的数值。
  • train_acc += ...: 计算并累加准确率。
    • np.argmax(..., axis=1): 取出预测概率最大的类别的索引。
    • == labels...: 与真实标签比较,返回 True/False。
    • np.sum(...): 统计预测正确的数量。

6. 记录训练数据

python

编辑

        plt_train_loss . append(train_loss/train_loader.dataset.__len__())
        plt_train_acc.append(train_acc/train_loader.dataset.__len__())
  • 将当前 epoch 的平均 Loss 和平均准确率加入列表。
  • 注意:这里除以 dataset.__len__() 是为了求平均值(虽然通常除以 batch 数量更常见,但除以总样本数也是一种归一化方式)。

7. 验证阶段 (Validation Phase)

python

编辑

        if i % val_epoch == 0:
            model.eval()
            with torch.no_grad():
                for batch in tqdm(val_loader):
                    val_text, val_labels = batch[0], batch[1].to(device)
                    val_pred = model(val_text)
                    val_bat_loss = loss(val_pred, val_labels)
                    val_loss += val_bat_loss.cpu().item()

                    val_acc += np.sum(np.argmax(val_pred.cpu().data.numpy(), axis=1) == val_labels.cpu().numpy())
                    val_rel.append(val_pred)
  • if i % val_epoch == 0:: 判断是否到了验证的轮次(这里 val_epoch=1,所以每轮都验证)。
  • model.eval(): 关键! 将模型设置为评估模式。这会关闭 Dropout,确保预测结果稳定。
  • with torch.no_grad():: 停止梯度计算。验证时不需要更新模型,所以关闭梯度可以节省显存和计算资源。
  • for batch in tqdm(val_loader):: 遍历验证集数据。
  • val_pred = model(val_text): 前向传播得到预测结果。
  • val_loss += ...: 累加验证集 Loss。
  • val_acc += ...: 累加验证集预测正确的数量。

8. 保存最佳模型与打印日志

python

编辑

            if val_acc > max_acc:
                torch.save(model, save_path+str(epoch)+"ckpt")
                max_acc = val_acc
            plt_val_loss.append(val_loss/val_loader.dataset.__len__())
            plt_val_acc.append(val_acc/val_loader.dataset.__len__())
            print('[%03d/%03d] %2.2f sec(s) TrainAcc : %3.6f TrainLoss : %3.6f | valAcc: %3.6f valLoss: %3.6f  ' % \
                  (i, epoch, time.time()-start_time, plt_train_acc[-1], plt_train_loss[-1], plt_val_acc[-1], plt_val_loss[-1])
                  )
  • if val_acc > max_acc:: 如果当前验证集的准确率超过了历史最高记录。
  • torch.save(model, ...): 保存模型。将当前模型权重保存到硬盘。
  • max_acc = val_acc: 更新最高准确率记录。
  • plt_val_loss.append(...): 记录验证集的 Loss 和准确率到绘图列表。
  • print(...): 打印当前轮次的状态信息,包括:
    • 轮次进度 [i/epoch]
    • 耗时 sec(s)
    • 训练集准确率和 Loss
    • 验证集准确率和 Loss

9. 非验证轮次的处理

python

编辑

            if i % 50 == 0:
                torch.save(model, save_path+'-epoch:'+str(i)+ '-%.2f'%plt_val_acc[-1])
  • 每隔 50 轮额外保存一次模型快照(虽然这里 epoch 只有 5,所以这行代码实际上不会触发,除非训练很多轮)。

python

编辑

        else:
            plt_val_loss.append(plt_val_loss[-1])
            plt_val_acc.append(plt_val_acc[-1])
            print('[%03d/%03d] %2.2f sec(s) TrainAcc : %3.6f TrainLoss : %3.6f   ' % \
                  (i, epoch, time.time()-start_time, plt_train_acc[-1], plt_train_loss[-1])
                  )
  • else:: 如果这一轮不需要验证(即 i % val_epoch != 0)。
  • plt_val_loss.append(plt_val_loss[-1]): 为了保持绘图列表长度一致,复制上一次验证的结果填进去(这样画图时 x 轴不会错位)。
  • print(...): 只打印训练集的信息。

10. 绘图 (Visualization)

python

编辑

    plt.plot(plt_train_loss)
    plt.plot(plt_val_loss)
    plt.title('loss')
    plt.legend(['train', 'val'])
    plt.show()
  • 训练结束后,使用 matplotlib 画出 Loss 曲线。
  • plt.plot(...): 绘制线条。
  • plt.legend(...): 显示图例,区分训练 Loss 和验证 Loss。
  • plt.show(): 显示图像。

python

编辑

    plt.plot(plt_train_acc)
    plt.plot(plt_val_acc)
    plt.title('Accuracy')
    plt.legend(['train', 'val'])
    plt.savefig('acc.png')
    plt.show()
  • 同样的逻辑,画出准确率曲线。
  • plt.savefig('acc.png'): 将准确率曲线图保存为 acc.png 文件
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐