一、环境安装准备
#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}
]
#接口传入 》》如何查询我的订单状态?》》生成结果如下

#接口传入 》》如何进行退货操作?》》生成结果如下
