[1]:
import torch
import numpy as np
import os
from EduNLP.Pretrain import DisenQTokenizer, train_disenqnet
from EduNLP.Vector import DisenQModel, T2V
from EduNLP.I2V import DisenQ, get_pretrained_i2v
from EduNLP.ModelZoo import load_items
os.environ["WANDB_DISABLED"] = "true"
d:\MySoftwares\Anaconda\envs\data\lib\site-packages\gensim\similarities\__init__.py:15: UserWarning: The gensim.similarities.levenshtein submodule is disabled, because the optional Levenshtein package <https://pypi.org/project/python-Levenshtein/> is unavailable. Install Levenhstein (e.g. `pip install python-Levenshtein`) to suppress this warning.
warnings.warn(msg)
训练自己的disenQNet模型¶
1. 数据¶
[2]:
BASE_DIR = "../.."
data_dir = f"{BASE_DIR}/static/test_data"
output_dir = f"{BASE_DIR}/examples/test_model/disenq"
disen_data_train = load_items(f"{data_dir}/disenq_train.json")
disen_data_test = load_items(f"{data_dir}/disenq_test.json")
2. 训练和评估¶
[3]:
tokenizer = DisenQTokenizer(max_length=250, tokenize_method="space")
train_params = {
# data params
"trim_min": 2,
"w2v_workers": 1,
# model params
"hidden": 128,
"dropout": 0.2,
"pos_weight": 1,
"cp": 1.5,
"mi": 1.0,
"dis": 2.0,
# training params
"epoch": 1,
"batch": 64,
"lr": 1e-3,
"step": 20,
"gamma": 0.5,
"warm_up": 1,
"adv": 10,
"device": "cpu"
}
data_formation = {
"content": "content",
"knowledge": "knowledge"
}
train_disenqnet(
disen_data_train,
tokenizer,
output_dir,
output_dir,
train_params=train_params,
test_items=disen_data_test,
)
load vocab from ../../examples/test_model/disenq\vocab.list
load concept from ../../examples/test_model/disenq\concept.list
load word2vec from ../../examples/test_model/disenq\wv.th
processing raw data for QuestionDataset...
vocab size: 6827
concept size: 5
load vocab from ../../examples/test_model/disenq\vocab.list
load concept from ../../examples/test_model/disenq\concept.list
load word2vec from ../../examples/test_model/disenq\wv.th
processing raw data for QuestionDataset...
Start training the disenQNet...
[Epoch 1] train loss: 1.5397
[Epoch 2] train loss: 1.5176, eval loss: 1.5289
3.使用模型¶
3.1 使用I2V将题目转为向量¶
[4]:
tokenizer_kwargs = {
"tokenizer_config_dir": output_dir,
}
i2v = DisenQ('disenq', 'disenq', output_dir, tokenizer_kwargs=tokenizer_kwargs, device="cpu")
test_items = [
{"content": "10 米 的 (2/5) = 多少 米 的 (1/2),有 公 式"},
{"content": "10 米 的 (2/5) = 多少 米 的 (1/2),有 公 式 , 如 图 , 若 $x,y$ 满 足 约 束 条 件 公 式"},
]
t_vec = i2v.infer_token_vector(test_items, key=lambda x: x["content"])
i_vec_k = i2v.infer_item_vector(test_items, key=lambda x: x["content"], vector_type="k")
i_vec_i = i2v.infer_item_vector(test_items, key=lambda x: x["content"], vector_type="i")
print(t_vec.shape) # == torch.Size([2, 23, 128])
print(i_vec_k.shape) # == torch.Size([2, 128])
print(i_vec_i.shape) # == torch.Size([2, 128])
t_vec = i2v.infer_token_vector(test_items[0], key=lambda x: x["content"])
i_vec_k = i2v.infer_item_vector(test_items[0], key=lambda x: x["content"], vector_type="k")
i_vec_i = i2v.infer_item_vector(test_items, key=lambda x: x["content"], vector_type="i")
print(t_vec.shape) # == torch.Size([1, 11, 128])
print(i_vec_k.shape) # == torch.Size([1, 128])
print(i_vec_i.shape) # == torch.Size([2, 128])
torch.Size([2, 23, 128])
torch.Size([2, 128])
torch.Size([2, 128])
torch.Size([1, 11, 128])
torch.Size([1, 128])
torch.Size([2, 128])
3.2 使用DisenQTokenizer先分词,再用T2V向量化¶
使用DisenQTokenizer¶
[5]:
tokenizer = DisenQTokenizer.from_pretrained(output_dir)
# 对题目文本进行令牌化
items = [
"有 公 式 $\\FormFigureID{wrong1?}$ ,如 图 $\\FigureID{088f15ea-xxx}$",
"已知 圆 $x^{2}+y^{2}-6 x=0$ ,过 点 (1,2) 的 直 线 被 该 圆 所 截 得 的 弦 的 长度 的 最小 值 为"
]
tokenizer.set_vocab(items, silent=False)
# 可以对单个题目进行令牌化
print(tokenizer(items[0]))
print()
# 也可以对题目列表进行令牌化
token_items = tokenizer(items)
print(token_items)
print()
save words(trim_min_count=1): 27/27 = 1.0000 with frequency 31/31=1.0000
{'content_idx': tensor([[20, 10, 14, 4, 28, 11, 3]]), 'content_len': tensor([7])}
{'content_idx': tensor([[20, 10, 14, 4, 28, 11, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2],
[13, 12, 5, 7, 29, 21, 6, 22, 23, 24, 25, 26, 12, 18, 17, 16, 22, 15,
22, 27, 22, 19, 9, 8]]), 'content_len': tensor([ 7, 24])}
[6]:
# 可以使用tokenize方法查看令牌化后的文本
print(tokenizer.tokenize(items[0]))
print(tokenizer.tokenize(items))
['有', '公', '式', '$\\FormFigureID{wrong1?}$', ',如', '图', '$\\FigureID{088f15ea-xxx}$']
[['有', '公', '式', '$\\FormFigureID{wrong1?}$', ',如', '图', '$\\FigureID{088f15ea-xxx}$'], ['已知', '圆', '$x^{2}+y^{2}-6', 'x=0$', ',过', '点', '(1,2)', '的', '直', '线', '被', '该', '圆', '所', '截', '得', '的', '弦', '的', '长度', '的', '最小', '值', '为']]
使用T2V加载模型¶
[7]:
pretrained_dir = f"{BASE_DIR}/examples/test_model/disenq"
t2v = DisenQModel(pretrained_dir)
token_items = tokenizer(items)
# 获得句表征和词表征
t_vec, i_vec_k, i_vec_i = t2v(token_items)
print(i_vec_k.shape, i_vec_i.shape)
print(t_vec.shape)
print()
# 获得词表征
t_vec = t2v.infer_tokens(token_items)
# 获得句表征
i_vec_k, i_vec_i = t2v.infer_vector(token_items)
# 获得句表征
i_vec_k = t2v.infer_vector(token_items, vector_type="k")
i_vec_i = t2v.infer_vector(token_items, vector_type="i")
torch.Size([2, 128]) torch.Size([2, 128])
torch.Size([2, 24, 128])
3.3 使用EduNLP中公开的预训练模型¶
[8]:
# 获取公开的预训练模型
pretrained_dir = f"{BASE_DIR}/examples/test_model/disenq"
i2v = get_pretrained_i2v("disenq_test_128", model_dir=pretrained_dir)
EduNLP, INFO model_dir: ..\..\examples\test_model\disenq\disenq_test_128
EduNLP, INFO Use pretrained t2v model disenq_test_128
downloader, INFO http://base.ustc.edu.cn/data/model_zoo/modelhub/disenq_pub/1/disenq_test_128.zip is saved as ..\..\examples\test_model\disenq\disenq_test_128.zip
Downloading ..\..\examples\test_model\disenq\disenq_test_128.zip 100.00%: 4.78MB | 4.78MB
downloader, INFO ..\..\examples\test_model\disenq\disenq_test_128.zip is unzip to ..\..\examples\test_model\disenq\disenq_test_128
[9]:
test_items = [
"有 公 式 $\\FormFigureID{1}$ ,如 图 $\\FigureID{088f15ea-xxx}$",
"已知 圆 $x^{2}+y^{2}-6 x=0$ ,过 点 (1,2) 的 直 线 被 该 圆 所 截 得 的 弦 的 长度 的 最小 值 为"
]
# 获得句表征和词表征
i_vec, t_vec = i2v(test_items)
print(i_vec[0].shape, i_vec[1].shape)
print(t_vec.shape)
print()
i_vec_k, t_vec = i2v(test_items, vector_type="k")
print(i_vec_k.shape)
print(t_vec.shape)
print()
# 获得指定表征
i_vec_k = i2v.infer_item_vector(test_items, vector_type="k")
i_vec_i = i2v.infer_item_vector(test_items, vector_type="i")
t_vec = i2v.infer_token_vector(test_items)
print(i_vec_k.shape)
print(i_vec_i.shape)
print(t_vec.shape)
torch.Size([2, 128]) torch.Size([2, 128])
torch.Size([2, 24, 128])
torch.Size([2, 128])
torch.Size([2, 24, 128])
torch.Size([2, 128])
torch.Size([2, 128])
torch.Size([2, 24, 128])
[10]:
test_items2 = [
"有公式$\\FormFigureID{wrong1?}$,如图$\\FigureID{088f15ea-xxx}$,\
若$x,y$满足约束条件公式$\\FormFigureBase64{wrong2?}$,$\\SIFSep$,则$z=x+7 y$的最大值为$\\SIFBlank$",
"已知圆$x^{2}+y^{2}-6 x=0$,过点(1,2)的直线被该圆所截得的弦的长度的最小值为"
]
print("The text tokenization method of pretrained i2v: ",i2v.tokenizer.tokenize_method)
# if the test data is note the same formation as train data, you can change tokenzer_method! But it's not recommended.
i2v.tokenizer.set_text_tokenizer("pure_text")
print("Reset the text tokenization method of pretrained i2v: ",i2v.tokenizer.tokenize_method)
i_vec, t_vec = i2v(test_items2)
print(i_vec[0].shape, i_vec[1].shape)
print(t_vec.shape)
print()
The text tokenization method of pretrained i2v: space
Reset the text tokenization method of pretrained i2v: pure_text
torch.Size([2, 128]) torch.Size([2, 128])
torch.Size([2, 25, 128])