实现Django和Transformers 构建智能客服大模型(模拟订单系统)

一、环境安装准备

   #git拉取 bert-base-chinese 文件
 
 
    #创建 虚拟运行环境
    python -m venv myicrplatenv
    #刷新
    source myicrplatenv/bin/activate
    #python Django 集成nacos
    pip install nacos-sdk-python
    #安装 Django
    pip3 install Django==5.1
    #安装 pymysql settings.py 里面需要 
    # 强制用pymysql替代默认的MySQLdb pymysql.install_as_MySQLdb()
    pip install pymysql
    # 安装mongo
    pip install djongo pymongo
    pip install transformers
    pip install torch
    #安装Daphne: 
    pip install daphne
    #项目通过 通过 daphne启动
    daphne icrplat.asgi:application

二、构建项目及app模块

#创建app模块
python3 manage.py startapp cs

icrplat
 
├── README.md
├── cs
│   ├── __init__.py
│   ├── __pycache__
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── icrplat
│   ├── __init__.py
│   ├── __pycache__
│   ├── asgi.py
│   ├── common
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
├── myicrplatenv
│   ├── bin
│   ├── include
│   ├── lib
│   ├── pyvenv.cfg
│   └── share
├── nacos-data
│   └── snapshot
└── templates

三、相关代码

####################################views.py####################################

@csrf_exempt
def send_message(request):
    if request.method == "GET":
        text = request.GET.get("message")

        # 输入验证
        if not text or len(text) > 512:
            return JsonResponse({"status": "error", "message": "无效的输入文本"})

        try:

            # 加载 BERT 模型和分词器
            path = "/Users/jiajiamao/soft/python/space/bert-base-chinese"
            model_dir = "/Users/jiajiamao/soft/python/space/csBotModel/"
            model_path = os.path.join(model_dir, "cs_bot_model.pth")

            tokenizer = BertTokenizer.from_pretrained(path)
            model = BertModel.from_pretrained(path)

            # 初始化训练类
            trainer = TrainModel(tokenizer, model, output_size=3)

            trainer.train_dataloader()

            # 加载训练好的模型
            trainer.load_model(model_path)

            # 调用模型进行推理
            predicted_label = trainer.predict(text)
            print(f"Predicted Label: {predicted_label}")

            # 根据预测结果生成回复
            if predicted_label == 0:
                response = "您的订单状态是:已发货。"
            elif predicted_label == 1:
                response = "您可以取消订单,请提供订单号。"
            elif predicted_label == 2:
                response = "请登录您的账户,提交退货申请。"
            else:
                response = "抱歉,我不明白您的问题。"

            return JsonResponse({"status": "success", "response": response})
        except Exception as e:
            return JsonResponse({"status": "error", "message": str(e)})
    else:
        return JsonResponse({"status": "error", "message": "无效的请求方法"})

################################CsBotModule.py################################


import torch.nn as nn


"""
    定义模型类 CsBotModel
"""
class CsBotModule(nn.Module):
    """
           nn.Module:所有神经网络模型的基类。自定义模型需要继承 nn.Module 并实现 __init__ 和 forward 方法。
           @:param bert_model 预训练的 BERT 模型(例如 BertModel 或类似的 Hugging Face 模型)
           @:param hidden_size:BERT 模型的隐藏层大小(通常是 768 或 1024)
           @:param output_size:输出层的大小,表示回复的分类数量或生成回复的维度。
       """
    def __init__(self, bert_model, hidden_size, output_size):
        super(CsBotModule, self).__init__()
        self.bert = bert_model  # 加载的 BERT 模型,用于提取输入文本的特征。
        self.fc = nn.Linear(hidden_size, output_size)  # 一个全连接层(线性层),将 BERT 的输出映射到最终的输出空间

    """
        forward方法用来定义数据如何通过模型传递,最终返回分类结果
        @:param input_ids:输入的 token ID 序列(经过 tokenizer 处理后的输入)。
        @:param attention_mask:注意力掩码,用于指示哪些 token 是有效的(1)或填充的(0)。
    """

    def forward(self, input_ids, attention_mask):
        # 获取BERT的输出
        outputs = self.bert(input_ids=input_ids, attention_mask=attention_mask)
        pooled_output = outputs.pooler_output  # 使用[CLS]的输出向量
        # 全连接层生成回复
        logits = self.fc(pooled_output)
        return logits

################################TrainModel.py################################



import logging
import os

import torch
import torch.nn as nn
from torch.optim import AdamW
from cs.CsBotModule import CsBotModule


"""
    PyTorch和Transformers库中的模块。
    torch是PyTorch的核心库,
    nn是用来构建神经网络的模块,
    BertTokenizer和BertModel是BERT模型和它的分词器,
    AdamW是用来优化模型的。
"""
class TrainModel:
    """
       #初始化模型  创建一个 CsBotModel 的实例
       @:param model:预训练的 BERT 模型。
       @:param hidden_size=768:BERT 模型的隐藏层大小(BERT-base 的隐藏层大小为 768)。
       @:param output_size=tokenizer.vocab_size:输出层的大小,设置为 tokenizer 的词汇表大小,表示模型需要生成词汇表中的每个 token 的概率。
    """
    def __init__(self, tokenizer, model,output_size):
        self.tokenizer = tokenizer
        self.model = model
        # self.csBotModule = CsBotModule(self.model, hidden_size=768, output_size=self.tokenizer.vocab_size)
        self.csBotModule = CsBotModule(self.model, hidden_size=768, output_size=output_size)

    """
             数据预处理
        """
    # def preprocess_data(self, dataList, max_len=512):
    #     input_ids = []  # input_ids:表示文本的ID序列。
    #     attention_masks = []  # attention_mask:表示哪些位置是真实文本,哪些位置是填充的
    #     labels = []  # labels:表示文本对应的类别标签
    #     for data in dataList:
    #         # TODO 对每一条文本数据,使用BERT的分词器(tokenizer)进行编码 将其转换为模型可以处理的格式
    #         encoded = self.tokenizer.encode_plus(
    #             data["text"],  # 需要编码的文本内容。
    #             max_length=max_len,  # 设定编码后的序列最大长度(这里为64)。如果文本长度超过这个值,会进行截断;如果不足,会用特殊符号填充。
    #             padding="max_length",  # 将序列填充到max_length指定的长度。
    #             truncation=True,  # 如果文本长度超过max_length,进行截断
    #             return_attention_mask=True,  # 返回注意力掩码(attention_mask),它告诉模型哪些位置是真实文本,哪些位置是填充的。
    #             return_tensors="pt"  # 返回PyTorch张量格式,方便后续直接用于模型训练
    #         )
    #
    #         input_ids.append(encoded["input_ids"])  # input_ids是BERT模型输入的ID序列,代表了分词后的文本。每个词或子词被映射成一个唯一的ID。
    #         attention_masks.append(encoded["attention_mask"])  # attention_mask是一个二进制掩码,用来区分真实文本和填充部分。真实文本位置为1,填充位置为0。
    #         labels.append(data["label"])  # 将继续将每一条文本对应的标签(label)添加到labels列表中
    #
    #     input_ids = torch.cat(input_ids,dim=0)  # 通过torch.cat方法将input_ids列表中的所有张量拼接成一个大的张量,适合批量输入模型。dim=0表示沿着第一维度(行)进行拼接
    #     attention_masks = torch.cat(attention_masks, dim=0)  # 将attention_masks列表中的所有张量拼接成一个大的张量
    #     labels = torch.tensor(labels)  # 将labels列表转换为一个PyTorch张量,方便后续计算损失函数
    #
    #     return input_ids, attention_masks, labels
    def preprocess_data(self, dataList, max_len=512):
        input_ids = []
        attention_masks = []
        labels = []
        for data in dataList:
            encoded = self.tokenizer.encode_plus(
                data["text"],
                max_length=max_len,
                padding="max_length",
                truncation=True,
                return_attention_mask=True,
                return_tensors="pt"
            )
            input_ids.append(encoded["input_ids"])
            attention_masks.append(encoded["attention_mask"])
            labels.append(data["label"])

        input_ids = torch.cat(input_ids, dim=0)
        attention_masks = torch.cat(attention_masks, dim=0)
        labels = torch.tensor(labels)
        return input_ids, attention_masks, labels

    """
        训练循环
            将模型设置为训练模式。
            清空梯度。
            输入数据并得到模型的预测值。
            计算损失值。
            通过反向传播计算梯度。
            使用优化器更新模型参数。
            打印当前轮次和损失值。
            通过多次循环,模型会逐渐拟合数据,损失值也会逐渐降低,最终达到我们需要的效果。
    """
    def train_dataloader(self):
        """
            定义损失函数和优化器
            损失函数:nn.CrossEntropyLoss():交叉熵损失函数,用于分类任务或多分类生成任务。
                    它将模型的输出(logits)与真实标签(labels)进行比较,计算损失。
            优化器:AdamW(chatbot_model.parameters(), lr=5e-5):使用 AdamW 优化器,
                   学习率设置为 5e-5(BERT 模型的典型学习率)。chatbot_model.parameters():获取模型的所有可训练参数。
        """
        criterion = nn.CrossEntropyLoss()
        optimizer = AdamW(self.csBotModule.parameters(), lr=5e-5)

        epochs = 10  # 增加训练轮数
        dataList = [
            {"text": "如何查询我的订单状态?", "label": 0},
            {"text": "我可以取消订单吗?", "label": 1},
            {"text": "如何进行退货操作?", "label": 2},
            {"text": "我的订单在哪里?", "label": 0},
            {"text": "订单支付失败了怎么办?", "label": 1},
            {"text": "我的订单显示已发货,但还没收到。", "label": 2}
        ]
        input_ids, attention_masks, labels = self.preprocess_data(dataList, max_len=512)  # 修正参数顺序
        # csBotModel = self.init_model()
        for epoch in range(epochs):
            # 将模型设置为训练模式。在训练模式下,模型会启用Dropout等特性,并计算梯度用于反向传播
            self.csBotModule.train()
            # 在每一轮训练开始时,清空优化器中的梯度。这一步非常重要,因为如果不清空梯度,梯度会在每一轮中累积,导致训练结果错误
            optimizer.zero_grad()
            # 将预处理好的数据(input_ids和attention_masks)输入模型,模型会计算出预测值(outputs)。
            outputs = self.csBotModule(input_ids, attention_masks)
            # 计算模型的损失值(loss)。criterion是损失函数,这里使用的是CrossEntropyLoss(交叉熵损失),
            # 它用于衡量模型的预测值(outputs)与实际标签(labels)之间的差异。损失值越小,说明模型的预测越接近真实值
            loss = criterion(outputs, labels)
            # 进行反向传播,计算损失的梯度。这一步会从损失值开始,沿着模型的每一层,逐层计算各参数的梯度。
            loss.backward()
            # 根据计算出的梯度,使用优化器(这里用的是AdamW)更新模型的参数,从而让模型在下一轮训练中表现得更好。
            optimizer.step()
            logging.info((f"*********************Epoch {epoch + 1}, Loss: {loss.item()}"))

        # 检查目录是否存在,如果不存在则创建
        model_dir = "/Users/jiajiamao/soft/python/space/csBotModel/"
        os.makedirs(model_dir, exist_ok=True)

        # 保存模型
        model_path = os.path.join(model_dir, "cs_bot_model.pth")
        self.save_model(model_path)

    """
        保存模型
    """
    def save_model(self, path):
        torch.save(self.csBotModule.state_dict(), path)

    """
        # 加载模型
    """
    def load_model(self, path):
        self.csBotModule.load_state_dict(torch.load(path))
        self.csBotModule.eval()

    # 推理
    def predict(self, text):
        self.csBotModule.eval()
        with torch.no_grad():
            encoded = self.tokenizer.encode_plus(
                text,
                max_length=512,
                padding="max_length",
                truncation=True,
                return_attention_mask=True,
                return_tensors="pt"
            )
            input_ids = encoded["input_ids"]
            attention_mask = encoded["attention_mask"]
            outputs = self.csBotModule(input_ids, attention_mask)
            _, predicted = torch.max(outputs, dim=1)
            return predicted.item()

四、启动项目

#通过 daphne 启动应用
daphne icrplat.asgi:application

五、测试

# 涉及到的数据都是 模拟的,可以改成动态数据


  epochs = 10  # 增加训练轮数
        dataList = [
            {"text": "如何查询我的订单状态?", "label": 0},
            {"text": "我可以取消订单吗?", "label": 1},
            {"text": "如何进行退货操作?", "label": 2},
            {"text": "我的订单在哪里?", "label": 0},
            {"text": "订单支付失败了怎么办?", "label": 1},
            {"text": "我的订单显示已发货,但还没收到。", "label": 2}
        ]

#接口传入  》》如何查询我的订单状态?》》生成结果如下
#接口传入 》》如何进行退货操作?》》生成结果如下
相关推荐
AntBlack4 分钟前
Python 打包笔记 : 你别说 ,PyStand 确实简单易上手
后端·python·创业
xiaozaq20 分钟前
Spring Boot静态资源访问顺序
java·spring boot·后端
熬夜苦读学习35 分钟前
库制作与原理
linux·数据库·后端
Pocker_Spades_A38 分钟前
Python刷题:Python基础
开发语言·python
上官-王野1 小时前
大模型day01自然语言+大模型+环境
python·ai·conda
uhakadotcom1 小时前
Pandas入门:数据处理和分析的强大工具
后端·面试·github
Asthenia04121 小时前
Json里面想传图片(验证码图)-Base64编码来助你!
后端
服务端技术栈2 小时前
MySQL 索引:数据库查询的“加速器”
后端
dowhileprogramming2 小时前
Flask 框架简介
python·flask