作为目前较真实且auc较高的的知识追踪模型,来看看它的代码结构,方便你我去实现和修改。原理、创新点请看论文,本文只作代码分析:
目的
AKT在assistment15(0.782)数据上比DKT(0.731)要高很多,同样只使用技能信息的情况下:
我的任务就是看为什么auc高这么多,文章没有具体消融试验,但可以从一部分看出
对技能和交互先encoder一次,在assist15得到5%的提升
使用了衰减机制,在assist15得到5%的提升。
问题出现了,究竟是encoder起的作用,还是衰减机制起的作用。使其达到0.7228
我估计是这里的AKT-NR-pos是相对于AKT-NR-raw的基础上在去掉衰减机制,替换成position embedding。
也可以看到AKT-NR-raw为0.7332,而AKT-NR-pos为0.7271。所以就算CCF-A也不不公平地比较。不是严格按照:每个对比实验只有一个组件不同。
结论:应该是encoder起的作用大一点。
数据集
作者处理成 一个样本行数据:(id是连续)
- 学生id,学生真实索引
- 题目id
- 技能id
- 答对答错
样本以学生为单位,分成训练集,验证集,测试集。(60%,20%,20%)
如果嫌数据量太少,把验证集归到训练集,因为代码要跑验证部分,避免修改结构,把测试集赋给验证集。
main.py 总体
- 加载数据的类,分DATA和PID_DATA,有的数据集没有技能信息
- 设置种子数seed
- params.train_set,5折交叉验证,有5个训练集
- train_q_data, train_qa_data, train_pid = dat.load_data(train_data_path)与DKVMN一样。而DKT不需要seq_len-1(不预测第一题)
- best_epoch = train_one_dataset()训练模型,记录最好的epoch
- test_one_dataset(best_epoch )使用最好的epoch预测测试集
train_one_dataset训练步骤
- 加载模型,在utils.py文件里,return model = AKT()
- 设置optimizer,包括模型参数、学习率
- for idx in range(params.max_iter)里面执行train函数。参数包括模型、数据,优化器,超参数
- 验证集auc提升时,保存模型、优化器。设置提前终止条件。
- f_save_log.write()记录结果
test_one_dataset测试步骤
- 加载模型及最优epoch的模型参数
- 执行一次test函数。参数没有优化器
- 运行完之后又把模型参数给删除
load_data.py
class DATA(object):
其中len(Q[len(Q)-1]) == 0,这一步是怕字符串根据分隔符分割之后,列表的最后一个元素为空字符。如:"1,2,3,"这样分割就会出现4个元素。
for lineID, line in enumerate(f_data):
记录student_id,第几个学生(样本)
记录Q,回答的技能
记录A,回答的情况
# 处理数据
1.一个学生截断成多个学生(题目数多于200道就认为新的样本),这里没有限制样本最小题目数
2.答题情况 = 2倍技能数,前半部代表答错,后半部代表答对
3.q_dataArray = np.zeros((len(q_data), self.seqlen)) 固定输入数据的大小,有数据的地方就填充,没有就为0
4.返回 q_dataArray, qa_dataArray, np.asarray(idx_data),idx_data是学生的id,模型应该没有用到的,只是用来保证return3个数据。
前两个是二维【batch,seq_len】 idx_data是一维【batch】
class PID_DATA(object):
只不过多处理了一行:题目信息
q_data.append(question_sequence) 技能信息
qa_data.append(answer_sequence) 答题情况
p_data.append(problem_sequence) 问题信息
return q_dataArray, qa_dataArray, p_dataArray
run.py
数据值域:id从1开始,0的位置为padding
train函数
- Shuffle the data,pid_flag是否有题目信息
- 数据包括input_q、input_pid、(input_qa、target)同一个,输入网络和训练目标
- target = (target - 1) / params.n_question,np.floor(target),使padding值为-1,非padding值为0或1
- loss, pred, true_ct = net(),这里的loss是用来反向传播
- nopadding_index = np.flatnonzero(target >= -0.9),根据索引就能筛选出要的预测值:pred[nopadding_index]
- 训练完全部数据后对all_pred,all_target计算auc、acc、loss(对全部数据算二值交叉熵)
test函数,区别在于
net.eval()
with torch.no_grad():
akt.py
模型部分不解释,就是self-attention、注意力衰减,只分析输入部分
参数部分
parser.add_argument('--d_model', type=int, default=256,
help='Transformer d_model shape')
parser.add_argument('--d_ff', type=int, default=1024,
help='Transformer d_ff shape')
parser.add_argument('--dropout', type=float,
default=0.05, help='Dropout rate')
parser.add_argument('--n_block', type=int, default=1,
help='number of blocks')
parser.add_argument('--n_head',