意图识别模型微调训练 根据自己准备的数据CPU就能训练微调意图模型-亲测验证有效
意图识别作用
在问答对话中,准确理解用户的意图是构建有效回答的关键。意图识别,即 判断用户想要什么 ,相当于为系统定向选择场景,帮助系统更精确的选择回复路径。
意图识别模型使用 基于BERT的对话意图和槽位联合识别 CPU运行BERT模型-亲测成功
运行环境
Python 3.8
下载代码
git clone https://github.com/Linear95/bert-intent-slot-detector.git
pycharm开发工具导入项目
训练数据准备
示例代码里自带了测试数据在:data/SMP2019下,我们参考这些测试数据,根据自己的业务制作训练数据
训练数据:
以json格式给出,每条数据包括三个关键词:
text表示待检测的文本,
intent代表文本的类别标签,
slots是文本中包括的所有槽位以及对应的槽值,以字典形式给出。
在data/路径下,新创建目录如:data/20241031。
训练数据放入data/20241031/train.json文件中,部分示例数据例如下:
{
"text": "打开空调",
"domain": "DEVICE_CONTROL",
"intent": "DEVICE_CONTROL",
"slots": {
"question": "打开空调",
"device_desc": "空调"
}
},
{
"text": "帮我生成一首诗",
"domain": "COMMON",
"intent": "COMMON",
"slots": {
"question": "帮我生成一首诗"
}
}
复制data/SMP2019/split_data.py放入data/20241031目录,然后运行,会将20241031的所有数据拆分成一个训练集split_train.json和一个测试集split_test.json
运行split_data.py程序 报错时:UnicodeDecodeError: ‘gbk’ codec can’t decode byte 0x80 in position 35: illegal multibyte sequence
增加编码格式:encoding=‘utf-8’
open(‘train.json’, ‘r’, encoding=‘utf-8’) as f
生产意图标签和槽位标签
复制data/SMP2019/extract_labels.py放入data/20241031目录,然后运行extract_labels.py程序,会生成意图标签intent_labels.txt文件和槽位标签slot_labels.txt文件。
同样报错时:UnicodeDecodeError: ‘gbk’ codec can’t decode byte 0x80 in position 35: illegal multibyte sequence
同样也是要增加编码格式:encoding=‘utf-8’
open(‘train.json’, ‘r’, encoding=‘utf-8’) as f
- 意图标签:
以txt格式给出,每行一个意图,未识别意图以[UNK]标签表示。在data/20241031/intent_labels.txt文件下:
[UNK]
COMMON
CARD_QUERY
DEVICE_QUERY
...
- 槽位标签:
与意图标签类似,以txt格式给出。包括三个特殊标签: [PAD]表示输入序列中的padding token, [UNK]表示未识别序列标签, [O]表示没有槽位的token标签。对于有含义的槽位标签,又分为以’B_'开头的槽位开始的标签, 以及以’I_'开头的其余槽位标记两种。
在data/20241031/slot_labels.txt文件下:
[PAD]
[UNK]
[O]
I_question
B_question
I_device_desc
B_device_desc
I_name
B_name
...
根据准备的数据,训练意图模型
可以直接修改train.py代码,然后运行
if __name__ == '__main__':
#训练自己的数据,生成意图模型
parser = argparse.ArgumentParser()
# environment parameters
parser.add_argument("--cuda_devices", type=str, default='0', help='set cuda device numbers')
parser.add_argument("--no_cuda", action='store_true', default=False, help='whether use cuda device for training')
# model parameters
parser.add_argument("--tokenizer_path", type=str, default='bert-base-chinese',
help="pretrained tokenizer loading path")
parser.add_argument("--model_path", type=str, default='bert-base-chinese', help="pretrained model loading path")
# data parameters
parser.add_argument("--train_data_path", type=str,
default='D:\PycharmProjects\\ai\\bert-intent-slot-detector\data\\20241031\split_train.json',
help="training data path")
parser.add_argument("--test_data_path", type=str,
default='D:\PycharmProjects\\ai\\bert-intent-slot-detector\data\\20241031\split_test.json',
help="testing data path")
parser.add_argument("--slot_label_path", type=str,
default='D:\PycharmProjects\\ai\\bert-intent-slot-detector\data\\20241031\slot_labels.txt',
help="slot label path")
parser.add_argument("--intent_label_path", type=str,
default='D:\PycharmProjects\\ai\\bert-intent-slot-detector\data\\20241031\intent_labels.txt',
help="intent label path")
# training parameters
parser.add_argument("--save_dir", type=str, default='D:\PycharmProjects\\ai\\bert-intent-slot-detector\data\\20241031\saved_model',
help="directory to save the model")
parser.add_argument("--max_training_steps", type=int, default=0,
help='max training step for optimizer, if larger than 0')
parser.add_argument("--gradient_accumulation_steps", type=int, default=1,
help="number of updates steps to accumulate before performing a backward() pass.")
parser.add_argument("--saving_steps", type=int, default=300, help="parameter update step number to save model")
parser.add_argument("--logging_steps", type=int, default=10,
help="parameter update step number to print logging info.")
parser.add_argument("--eval_steps", type=int, default=10,
help="parameter update step number to print logging info.")
parser.add_argument("--saving_epochs", type=int, default=1, help="parameter update epoch number to save model")
parser.add_argument("--batch_size", type=int, default=32, help='training data batch size')
parser.add_argument("--train_epochs", type=int, default=5, help='training epoch number')
parser.add_argument("--learning_rate", type=float, default=5e-5, help='learning rate')
parser.add_argument("--adam_epsilon", type=float, default=1e-8, help="epsilon for Adam optimizer")
parser.add_argument("--warmup_steps", type=int, default=0, help="warmup step number")
parser.add_argument("--weight_decay", type=float, default=0.0, help="weight decay rate")
parser.add_argument("--max_grad_norm", type=float, default=1.0, help="maximum norm for gradients")
args = parser.parse_args()
train(args)
也可以不修改代码,使用命令行动态传参的方式运行
可以使用以下命令进行模型训练,这里我们选择在bert-base-chinese预训练模型基础上进行微调:
python train.py \
--cuda_devices 0 \
--tokenizer_path "bert-base-chinese" \
--model_path "bert-base-chinese" \
--train_data_path "D:\PycharmProjects\\ai\\bert-intent-slot-detector\data\\20241031\split_train.json" \
--test_data_path "D:\PycharmProjects\\ai\\bert-intent-slot-detector\data\\20241031\split_test.json" \
--intent_label_path "D:\PycharmProjects\\ai\\bert-intent-slot-detector\data\\20241031\intent_labels.txt" \
--slot_label_path "D:\PycharmProjects\\ai\\bert-intent-slot-detector\data\\20241031\slot_labels.txt" \
--save_dir "D:\PycharmProjects\\ai\\bert-intent-slot-detector\data\\20241031\saved_model" \
--batch_size 32 \
--train_epochs 5
运行成功后会在data\20241031\saved_model生成微调后的模型
运行微调后的模型 测试意图识别能力
运行detector.py程序
if __name__ == '__main__':
model_path = 'data/20241031/saved_model/model/model_epoch4'
tokenizer_path = 'data/20241031/saved_model/tokenizer/'
intent_path = 'data/20241031/intent_labels.txt'
slot_path = 'data/20241031/slot_labels.txt'
model = JointIntentSlotDetector.from_pretrained(
model_path=model_path,
tokenizer_path=tokenizer_path,
intent_label_path=intent_path,
slot_label_path=slot_path
)
while True:
text = input("input: ")
print(model.detect(text))
验证识别效果
参考链接:https://github.com/Linear95/bert-intent-slot-detector