├── .idea ├── Knowledge_Tracing.iml ├── dictionaries │ └── linglingsu.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── Paper ├── Addressing Two Problems in Deep Knowledge Tracing via Prediction-Consistent Regularization.pdf ├── Deep Factorization Machines for Knowledge Tracing.pdf ├── Exercise-Enhanced Sequential Modeling for Student Performance Prediction.pdf ├── Going Deeper with Deep Knowledge Tracing .pdf ├── How Deep is Knowledge Tracing?.pdf ├── Incorporating rich features into Deep knowledge tracing.pdf ├── Knowledge Tracing Machines- Factorization Machines for Knowledge Tracing.pdf ├── Knowledge tracing Modeling the acquisition of procedural knowledge.pdf └── deep Knowledge Tracing.pdf ├── README.md ├── __init__.py └── code ├── DKT+ ├── __init__.py ├── metrics.py ├── model.py ├── model_params.py ├── run_dkt.py └── utils.py ├── DKT ├── __init__.py ├── metrics.py ├── model.py ├── model_params.py ├── run_dkt.py └── utils.py ├── TCN-KT ├── __init__.py ├── metrics.py ├── model.py ├── model_params.py ├── modules.py ├── run_dkt.py └── utils.py ├── Transformer-KT ├── __init__.py ├── metrics.py ├── model.py ├── model_params.py ├── modules.py ├── run_dkt.py └── utils.py └── data ├── 2016-EDM ├── 0910_a_test.csv ├── 0910_a_train.csv ├── 0910_b_test.csv ├── 0910_b_train.csv ├── 0910_c_test.csv ├── 0910_c_train.csv ├── 2015_builder_test.csv ├── 2015_builder_train.csv ├── CAT_test.csv └── CAT_train.csv ├── Assistment └── 2009-2010_skill_builder_data.csv ├── csv2tfrecords.py ├── eval.tfrecord └── train.tfrecord /.idea/Knowledge_Tracing.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /.idea/dictionaries/linglingsu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Paper/Addressing Two Problems in Deep Knowledge Tracing via Prediction-Consistent Regularization.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sulingling123/Knowledge_Tracing/e47425c068066190c0a435dc1ba1ce2c2a9c56ec/Paper/Addressing Two Problems in Deep Knowledge Tracing via Prediction-Consistent Regularization.pdf -------------------------------------------------------------------------------- /Paper/Deep Factorization Machines for Knowledge Tracing.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sulingling123/Knowledge_Tracing/e47425c068066190c0a435dc1ba1ce2c2a9c56ec/Paper/Deep Factorization Machines for Knowledge Tracing.pdf -------------------------------------------------------------------------------- /Paper/Exercise-Enhanced Sequential Modeling for Student Performance Prediction.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sulingling123/Knowledge_Tracing/e47425c068066190c0a435dc1ba1ce2c2a9c56ec/Paper/Exercise-Enhanced Sequential Modeling for Student Performance Prediction.pdf -------------------------------------------------------------------------------- /Paper/Going Deeper with Deep Knowledge Tracing .pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sulingling123/Knowledge_Tracing/e47425c068066190c0a435dc1ba1ce2c2a9c56ec/Paper/Going Deeper with Deep Knowledge Tracing .pdf -------------------------------------------------------------------------------- /Paper/How Deep is Knowledge Tracing?.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sulingling123/Knowledge_Tracing/e47425c068066190c0a435dc1ba1ce2c2a9c56ec/Paper/How Deep is Knowledge Tracing?.pdf -------------------------------------------------------------------------------- /Paper/Incorporating rich features into Deep knowledge tracing.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sulingling123/Knowledge_Tracing/e47425c068066190c0a435dc1ba1ce2c2a9c56ec/Paper/Incorporating rich features into Deep knowledge tracing.pdf -------------------------------------------------------------------------------- /Paper/Knowledge Tracing Machines- Factorization Machines for Knowledge Tracing.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sulingling123/Knowledge_Tracing/e47425c068066190c0a435dc1ba1ce2c2a9c56ec/Paper/Knowledge Tracing Machines- Factorization Machines for Knowledge Tracing.pdf -------------------------------------------------------------------------------- /Paper/Knowledge tracing Modeling the acquisition of procedural knowledge.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sulingling123/Knowledge_Tracing/e47425c068066190c0a435dc1ba1ce2c2a9c56ec/Paper/Knowledge tracing Modeling the acquisition of procedural knowledge.pdf -------------------------------------------------------------------------------- /Paper/deep Knowledge Tracing.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sulingling123/Knowledge_Tracing/e47425c068066190c0a435dc1ba1ce2c2a9c56ec/Paper/deep Knowledge Tracing.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Knowledge_Tracing 2 | - Paper : 知识追踪相关论文 3 | - [Deep Knowledge Tracing](https://github.com/ZoeYuhan/Knowledge_Tracing/blob/master/Paper/deep%20Knowledge%20Tracing.pdf): 4 | - 首次提出将RNN用于知识追踪,并能够基于复杂的知识联系进行建模(如构建知识图谱) 5 | - [How Deep is Knowledge Tracing](https://github.com/ZoeYuhan/Knowledge_Tracing/blob/master/Paper/How%20Deep%20is%20Knowledge%20Tracing%3F.pdf) 6 | - 探究DKT利用到的统计规律并拓展BKT,从而使BKT拥有能够与DKT相匹配的能力 7 | - [Going Deeper with Deep Knowledge Tracing](https://github.com/ZoeYuhan/Knowledge_Tracing/blob/master/Paper/Going%20Deeper%20with%20Deep%20Knowledge%20Tracing%20.pdf) 8 | - 对DKT和PFA,BKT进行了模型比较,对DKT模型能碾压其他两种模型的结果进行了怀疑并加以论证,进一步讨论了原论文能够得出上述结果的原因,对进一步使用DKT模型提供了参考。 9 | - [Incorporating Rich Features Into Deep Knowledge Tracing](https://github.com/ZoeYuhan/Knowledge_Tracing/blob/master/Paper/Incorporating%20rich%20features%20into%20Deep%20knowledge%20tracing.pdf) 10 | - 对DKT使用上进行数据层扩展,扩展学生和问题层的数据输入,包括结合自动编码器对输入进行转换 11 | - [Addressing Two Problems in Deep Knowledge Tracing viaPrediction-Consistent Regularization](https://github.com/ZoeYuhan/Knowledge_Tracing/blob/master/Paper/Addressing%20Two%20Problems%20in%20Deep%20Knowledge%20Tracing%20via%20Prediction-Consistent%20Regularization.pdf) 12 | - 指出DKT模型现存缺点:对输入序列存在重构问题和预测结果的波动性,进而对上述问题提出了改善方法 13 | - [Exercise-Enhanced Sequential Modeling for Student Performance Prediction](https://github.com/ZoeYuhan/Knowledge_Tracing/blob/master/Paper/Exercise-Enhanced%20Sequential%20Modeling%20for%20Student%20Performance%20Prediction.pdf) 14 | - 将题面信息引入,不仅作为输入送入模型,而且将题目编码后的向量计算cosine相似度作为atention的socre 15 | - [A Self-Attentive model for Knowledge Tracing](https://arxiv.org/pdf/1907.06837.pdf) 16 | - 使用Transformer应用于知识追踪 17 | ## Method 18 | | model | paper | 19 | | ---- | ---- | 20 | | DKT | [Deep Knowledge Tracing](https://github.com/ZoeYuhan/Knowledge_Tracing/blob/master/Paper/deep%20Knowledge%20Tracing.pdf) | 21 | | DKT+ | [Addressing Two Problems in Deep Knowledge Tracing viaPrediction-Consistent Regularization](https://github.com/ZoeYuhan/Knowledge_Tracing/blob/master/Paper/Addressing%20Two%20Problems%20in%20Deep%20Knowledge%20Tracing%20via%20Prediction-Consistent%20Regularization.pdf) | 22 | | TCN-KT| None | 23 | | Transformer-KT | [A Self-Attentive model for Knowledge Tracing](https://arxiv.org/pdf/1907.06837.pdf) | 24 | 25 | 26 | 27 | ## Usage : 28 | 29 | ```bash 30 | python DKT/run_dkt.py 31 | ``` 32 | 33 | ---- 34 | ### Acknowledgement 35 | - Blog: 36 | - [深度知识追踪](https://sulingling123.github.io/2019/08/06/%E6%B7%B1%E5%BA%A6%E7%9F%A5%E8%AF%86%E8%BF%BD%E8%B8%AA/) 37 | - [论文导读:Exercise-Enhanced Sequential Modeling for Student Performance Prediction](https://blog.csdn.net/Zoe_Su/article/details/84566409) -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:__init__.py.py 5 | @time:2019/2/19下午12:41 6 | """ -------------------------------------------------------------------------------- /code/DKT+/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:__init__.py.py 5 | @time:2019/10/811:10 AM 6 | """ -------------------------------------------------------------------------------- /code/DKT+/metrics.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:metrics.py 5 | @time:2019/8/122:57 PM 6 | """ 7 | import tensorflow as tf 8 | from model_params import * 9 | from model import DKT 10 | 11 | 12 | def dkt_loss(logits, target_correct, target_ids, correct, ids, seq_steps): 13 | """ 14 | calculate cross-entropy loss for dkt 15 | Args: 16 | logits: logits(no sigmoid func) with shape [batch_size, length, vocab_size] 17 | target_correct: targets with shape [batch_size, length] 18 | target_ids: 19 | seq_steps: 20 | 21 | Returns: 22 | cross-entropy loss 23 | 24 | """ 25 | with tf.name_scope('loss'): 26 | batch_size, length, vocab_size = tf.shape(logits)[0], tf.shape(logits)[1], tf.shape(logits)[2] 27 | flat_logits = tf.reshape(logits, [-1]) 28 | flat_target_correct = tf.reshape(target_correct, [-1]) 29 | 30 | flat_correctness = tf.reshape(correct, [-1]) 31 | 32 | flat_base_target_index = tf.range(batch_size * length) * vocab_size 33 | flat_bias_target_id = tf.reshape(target_ids, [-1]) 34 | flat_target_id = flat_base_target_index + flat_bias_target_id 35 | 36 | flat_target_logits = tf.gather(flat_logits, flat_target_id) 37 | mask = tf.reshape(tf.sequence_mask(seq_steps, maxlen=length), [-1]) 38 | # drop predict which user not react to 39 | flat_target_correct_mask = tf.boolean_mask(flat_target_correct, mask) 40 | flat_target_logits_mask = tf.boolean_mask(flat_target_logits, mask) 41 | 42 | loss = tf.reduce_mean(tf.nn.weighted_cross_entropy_with_logits( 43 | targets=flat_target_correct_mask, 44 | logits=flat_target_logits_mask, 45 | pos_weight=1 46 | )) 47 | 48 | r = tf.reduce_mean(tf.nn.weighted_cross_entropy_with_logits(targets=flat_correctness, 49 | logits=flat_target_logits, 50 | pos_weight=1)) 51 | pred_all = tf.sigmoid(logits) 52 | w1 = tf.reduce_mean(tf.abs(pred_all[:, :-1, :] - pred_all[:, 1:, :])) 53 | w2 = tf.reduce_mean(tf.square(pred_all[:, :-1, :] - pred_all[:, 1:, :])) 54 | loss += 0.5 * r + 0.1 * w1 + 0.1 * w2 55 | return loss 56 | 57 | 58 | def padded_accuracy(logits, target, target_ids, seq_len): 59 | """ 60 | Percentage of times that predictions matches albels on non-0s 61 | Args: 62 | logits: Tensor with shape [batch_size, length, vocab_size] 63 | target: Tesnor wtth shape [batch_size, length] 64 | 65 | Returns: 66 | 67 | """ 68 | with tf.variable_scope('padded_accuracy', values=[logits, target, target_ids, seq_len]): 69 | batch_size, length, vocab_size = tf.shape(logits)[0], tf.shape(logits)[1], tf.shape(logits)[2] 70 | flat_logits = tf.reshape(logits, [-1]) 71 | flat_target_correct = tf.reshape(target, [-1]) 72 | 73 | flat_base_target_index = tf.range(batch_size * length) * vocab_size 74 | flat_bias_target_id = tf.reshape(target_ids, [-1]) 75 | flat_target_id = flat_base_target_index + flat_bias_target_id 76 | 77 | flat_target_logits = tf.gather(flat_logits, flat_target_id) 78 | pred = tf.sigmoid(tf.reshape(flat_target_logits, [batch_size, length])) 79 | # self.binary_pred = tf.cast(tf.greater_equal(self.pred, 0.5), tf.int32) 80 | binary_pred = tf.cast(tf.greater(pred, 0.5), tf.float32) 81 | 82 | predict = tf.reshape(binary_pred, [-1]) 83 | mask = tf.reshape(tf.sequence_mask(seq_len, maxlen=tf.shape(logits)[1]), [-1]) 84 | flat_predict_mask = tf.boolean_mask(predict, mask) 85 | flat_target_mask = tf.boolean_mask(flat_target_correct, mask) 86 | return tf.equal(flat_target_mask, flat_predict_mask) 87 | 88 | 89 | def _convert_to_eval_metric(metric_fn): 90 | """ 91 | warper a metric_fn that returns scores and weights as an eval metric fn 92 | The input metric_fn returns values for the current batch. 93 | The wrapper aggregates the return values collected over all of the batches evaluated 94 | Args: 95 | metric_fn: function that return socres for current batch's logits and targets 96 | 97 | Returns: 98 | function that aggregates the score and weights from metric_fn 99 | 100 | """ 101 | def problem_metric_fn(*args): 102 | """ 103 | return an aggregation of the metric_fn's returned values 104 | Args: 105 | *args: 106 | 107 | Returns: 108 | 109 | """ 110 | score = metric_fn(*args) 111 | return tf.metrics.mean(score) 112 | return problem_metric_fn 113 | 114 | 115 | def get_eval_metrics(logits, labels, target_ids, seq_len): 116 | """ 117 | return dictionary of model evaluation metrics 118 | Args: 119 | logits: 120 | labels: 121 | params: 122 | 123 | Returns: 124 | 125 | """ 126 | metrics = { 127 | 'accuracy': _convert_to_eval_metric(padded_accuracy)(logits, labels, target_ids, seq_len), 128 | } 129 | return metrics -------------------------------------------------------------------------------- /code/DKT+/model.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:model.py 5 | @time:2019/10/83:13 PM 6 | """ 7 | from __future__ import absolute_import 8 | from __future__ import division 9 | from __future__ import print_function 10 | 11 | import tensorflow as tf 12 | 13 | 14 | class DKT(object): 15 | def __init__(self, params, train): 16 | """ 17 | Initialize layers to build DKT model 18 | Args: 19 | params: hyperparameter object defining layer size, dropout value etc 20 | train: boolean indicating whether the model is in training mode 21 | """ 22 | self.params = params 23 | self.train = train 24 | 25 | def __call__(self, batch_size, inputs, seq_len): 26 | initial_state = [] 27 | if self.params['use_one_hot']: 28 | self.embedding_output = tf.one_hot(inputs, depth=2*self.params['num_skills'] + 1) 29 | else: # Use Embedding 30 | embedding_table = tf.get_variable( 31 | name="word_embedding", 32 | shape=[2*self.params['num_skills']+1, self.params['hidden_size']], 33 | initializer=tf.truncated_normal_initializer(self.params['initializer_range'])) 34 | self.embedding_output = tf.nn.embedding_lookup(embedding_table, inputs) 35 | self.embedding_output = tf.contrib.layers.layer_norm(self.embedding_output, 36 | begin_norm_axis=-1, 37 | begin_params_axis=-1, scope='embedding_ln') 38 | self.embedding_output = tf.nn.dropout(self.embedding_output, keep_prob=self.params['keep_prob']) 39 | 40 | hidden_layers = [] 41 | final_hidden_size = self.params['hidden_size'] 42 | with tf.variable_scope('lstm_cell', initializer=tf.orthogonal_initializer()): 43 | for i in range(self.params['hidden_layer_num']): 44 | if self.params['use_LN']: 45 | hidden_layer = tf.contrib.rnn.LayerNormBasicLSTMCell(num_units=final_hidden_size, 46 | forget_bias=0.0, 47 | layer_norm=True, 48 | dropout_keep_prob=self.params['keep_prob']) 49 | else: 50 | hidden_layer = tf.contrib.rnn.BasicLSTMCell(num_units=final_hidden_size, forget_bias=0, 51 | state_is_tuple=True) 52 | # lstm_layer = tf.contrib.rnn.GRUCell(num_units=final_hidden_size, state_is_tuple=True) 53 | 54 | hidden_layer = tf.contrib.rnn.DropoutWrapper(cell=hidden_layer, 55 | output_keep_prob=self.params['keep_prob']) 56 | hidden_layers.append(hidden_layer) 57 | initial_state.append(hidden_layer.zero_state(batch_size, dtype=tf.float32)) 58 | self.hidden_cell = tf.contrib.rnn.MultiRNNCell(cells=hidden_layers, state_is_tuple=True) 59 | 60 | # dynamic rnn 61 | state_series, self.current_state = tf.nn.dynamic_rnn(cell=self.hidden_cell, 62 | inputs=self.embedding_output, 63 | sequence_length=seq_len, 64 | initial_state= tuple(initial_state)) 65 | 66 | output_w = tf.get_variable("weights", [final_hidden_size, self.params['num_skills']], 67 | initializer=tf.truncated_normal_initializer(self.params['initializer_range']), 68 | # regularizer=tf.contrib.layers.l2_regularizer(self.config.regularization_lambda) 69 | regularizer=None 70 | ) 71 | 72 | output_b = tf.get_variable("biases", [self.params['num_skills']], 73 | initializer=tf.zeros_initializer(), 74 | # regularizer=tf.contrib.layers.l2_regularizer(self.config.regularization_lambda) 75 | regularizer=None 76 | ) 77 | 78 | batch_size, max_len = tf.shape(inputs)[0], tf.shape(inputs)[1] 79 | self.state_series = tf.reshape(state_series, [batch_size * max_len, final_hidden_size]) 80 | 81 | logits = tf.matmul(self.state_series, output_w) + output_b 82 | logits = tf.reshape(logits, [batch_size, max_len, self.params['num_skills']]) 83 | # self.pred_all = tf.sigmoid(self.mat_logits) 84 | 85 | return logits -------------------------------------------------------------------------------- /code/DKT+/model_params.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:model_params.py 5 | @time:2019/10/83:31 PM 6 | """ 7 | from collections import defaultdict 8 | 9 | 10 | BASE_PARAMS = defaultdict( 11 | # Input params 12 | num_skills=125, 13 | 14 | # Model params 15 | initializer_range=0.02, # embedding table initializer 16 | keep_prob=0.5, 17 | use_LN=True, 18 | hidden_layer_num=4, 19 | hidden_size=128, 20 | use_one_hot=False, 21 | 22 | # Training params 23 | learning_rate=0.1, 24 | learning_rate_decay_rate=1.0, 25 | learning_rate_warmup_steps=16000, 26 | 27 | # Optimizer params 28 | optimizer_adam_beta1=0.9, 29 | optimizer_adam_beta2=0.997, 30 | optimizer_adam_epsilon=1e-09, 31 | 32 | ) -------------------------------------------------------------------------------- /code/DKT+/run_dkt.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:run_dkt.py 5 | @time:2019/8/1212:39 PM 6 | """ 7 | from __future__ import absolute_import 8 | from __future__ import division 9 | from __future__ import print_function 10 | 11 | import tensorflow as tf 12 | import os 13 | import numpy as np 14 | from model_params import BASE_PARAMS 15 | from model import * 16 | from metrics import * 17 | from utils import * 18 | 19 | 20 | 21 | flags = tf.flags 22 | logging = tf.logging 23 | 24 | flags.DEFINE_string(name='train_data', default='../data/train.tfrecord', 25 | help='Path of training data ') 26 | flags.DEFINE_string(name='valid_data', default='../data/eval.tfrecord', 27 | help='Path of valid data') 28 | flags.DEFINE_string(name='predict_data', default='', 29 | help='Path of predict data') 30 | flags.DEFINE_string(name='saved_model', default='./saved_model', help='Path to save model') 31 | flags.DEFINE_string(name='output_dir', default='./output_dir', help='Path to save model') 32 | flags.DEFINE_integer(name='epoch', default=100, help='Num of epoch') 33 | flags.DEFINE_integer(name='batch_size', default=1, help='Num of batch') 34 | flags.DEFINE_bool(name="do_train", default=True, help="Whether to run training.") 35 | flags.DEFINE_bool(name="do_eval", default=True, help="Whether to run eval on the dev set.") 36 | flags.DEFINE_bool( 37 | "do_predict", False, 38 | "Whether to run the model in inference mode on the test set.") 39 | flags.DEFINE_bool(name='pb', default=True, help='') 40 | FLAGS = flags.FLAGS 41 | 42 | 43 | def model_fn_builder(): 44 | def model_fn(features, labels, mode, params): 45 | """ 46 | define how to train, evaluate and predict from the transfomer model. 47 | Args: 48 | 49 | mode: 50 | params: 51 | 52 | Returns: 53 | 54 | """ 55 | inputs = features['inputs'] 56 | seq_steps = features['seq_len'] 57 | 58 | 59 | is_training = (mode == tf.estimator.ModeKeys.TRAIN) 60 | 61 | try: 62 | batch_size, length = get_shape_list(inputs, expected_rank=2) 63 | except ValueError: 64 | batch_size = 1 65 | length = get_shape_list(inputs, expected_rank=1)[0] 66 | inputs = tf.reshape(inputs, [batch_size, length]) 67 | 68 | with tf.variable_scope('model'): 69 | # Build model 70 | model = DKT(params, is_training) 71 | logits = model(batch_size, inputs, seq_steps) # [batch, length, vocab_size] 72 | 73 | # when in prediction mode, the label/target is Bone, the model output is the prediction 74 | if mode == tf.estimator.ModeKeys.PREDICT: 75 | export_outputs = {'predict_output': tf.estimator.export.PredictOutput({"predict": tf.sigmoid(logits)})} 76 | output_spec = tf.estimator.EstimatorSpec(mode=mode, 77 | predictions={'predict': tf.sigmoid(logits)}, 78 | export_outputs=export_outputs 79 | ) 80 | else: 81 | # Calculate model loss 82 | target_ids = features['target_id'] 83 | target_correct = features['target_correct'] 84 | ids = features['ids'] 85 | correct = features['correct'] 86 | loss = dkt_loss(logits, target_correct, target_ids, correct, ids, seq_steps) 87 | record_dict = {} 88 | record_dict['minibatch_loss'] = loss 89 | # Save loss as named tensor will be logged with the logging hook 90 | tf.identity(loss, 'cross_entropy') 91 | 92 | if mode == tf.estimator.ModeKeys.EVAL: 93 | metric_dict = get_eval_metrics(logits, target_correct, target_ids, seq_steps) 94 | record_dict['accuracy'] = metric_dict['accuracy'] 95 | record_scalars(record_dict) 96 | output_spec = tf.estimator.EstimatorSpec(mode=tf.estimator.ModeKeys.EVAL, 97 | loss=loss, 98 | predictions={'predict': tf.sigmoid(logits)}, 99 | eval_metric_ops=metric_dict ) 100 | else: # train 101 | # check whether restore from checkpoint 102 | tvars = tf.trainable_variables() 103 | initialized_variable_names = {} 104 | 105 | tf.logging.info("**** Trainable Variables ****") 106 | for var in tvars: 107 | init_string = "" 108 | if var.name in initialized_variable_names: 109 | init_string = ", *INIT_FROM_CKPT*" 110 | tf.logging.info(" name = %s, shape = %s%s", var.name, var.shape, 111 | init_string) 112 | 113 | train_op, metric_dict = get_train_op_and_metrics(loss, params) 114 | acc_metric = get_eval_metrics(logits, target_correct, target_ids, seq_steps) 115 | record_dict['accuracy'] = acc_metric['accuracy'] 116 | record_dict['learning_rate'] = metric_dict['learning_rate'] 117 | record_scalars(record_dict) 118 | output_spec = tf.estimator.EstimatorSpec(mode=tf.estimator.ModeKeys.TRAIN, 119 | loss=loss, 120 | train_op=train_op 121 | ) 122 | return output_spec 123 | return model_fn 124 | 125 | 126 | def input_fn_builder(datadir, epoch): 127 | def input_fn(): 128 | padded_shapes = {'inputs': [None], 129 | 'target_correct': [None], 130 | 'target_id': [None], 131 | 'ids': [None], 132 | 'correct': [None], 133 | 134 | 'seq_len': [], 135 | } 136 | 137 | dataset_train = get_dataset(datadir) 138 | dataset_train = dataset_train.repeat(epoch).shuffle(1000).padded_batch(FLAGS.batch_size, 139 | padded_shapes=padded_shapes) 140 | 141 | return dataset_train 142 | 143 | return input_fn 144 | 145 | 146 | def main(): 147 | # if not FLAGS.do_train and not FLAGS.do_eval and not FLAGS.do_predict: 148 | # raise ValueError("At least one of `do_train`, `do_eval` or `do_predict' must be True.") 149 | params = BASE_PARAMS # param dict from model_params.py 150 | tf.gfile.MakeDirs(FLAGS.saved_model) 151 | 152 | run_config = tf.estimator.RunConfig( 153 | model_dir=FLAGS.saved_model, 154 | tf_random_seed=tf.set_random_seed([42]), 155 | # save_summary_step=100, 156 | save_checkpoints_steps=1000, 157 | # save_checkpoints_secs=600, 158 | session_config=None, 159 | keep_checkpoint_max=5, 160 | keep_checkpoint_every_n_hours=10000, 161 | log_step_count_steps=100, 162 | train_distribute=None 163 | ) 164 | model_fn = model_fn_builder() 165 | estimator = tf.estimator.Estimator( 166 | model_fn=model_fn, 167 | model_dir=FLAGS.saved_model, 168 | config=run_config, 169 | params=params, 170 | warm_start_from=None 171 | ) 172 | # train_input_fn = input_fn_builder(FLAGS.train_data, FLAGS.epoch) 173 | # train_spec = tf.estimator.TrainSpec(input_fn=train_input_fn, 174 | # max_steps=None) 175 | # eval_input_fn = input_fn_builder(FLAGS.valid_data, 1) 176 | # eval_spec = tf.estimator.EvalSpec(input_fn=eval_input_fn, 177 | # steps=None, 178 | # throttle_secs=60) 179 | # tf.estimator.train_and_evaluate(estimator, 180 | # train_spec=train_spec, 181 | # eval_spec=eval_spec) 182 | for epoch in range(FLAGS.epoch): 183 | if FLAGS.do_train: 184 | train_input_fn = input_fn_builder(FLAGS.train_data, 1) 185 | 186 | estimator.train(input_fn=train_input_fn) 187 | if FLAGS.do_eval: 188 | eval_input_fn = input_fn_builder(FLAGS.valid_data, 1) 189 | result = estimator.evaluate(input_fn=eval_input_fn) 190 | 191 | output_eval_file = os.path.join(FLAGS.output_dir, "eval_results.txt") 192 | with tf.gfile.GFile(output_eval_file, "w+") as writer: 193 | tf.logging.info("***** Eval results *****") 194 | for key in sorted(result.keys()): 195 | tf.logging.info(" %s = %s", key, str(result[key])) 196 | writer.write("%s = %s\n" % (key, str(result[key]))) 197 | 198 | 199 | if __name__ == '__main__': 200 | tf.logging.set_verbosity(tf.logging.INFO) 201 | # flags.mark_flag_as_required("train_data") 202 | # flags.mark_flag_as_required("valid_data") 203 | # flags.mark_flag_as_required("saved_model") 204 | 205 | main() 206 | -------------------------------------------------------------------------------- /code/DKT+/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:utils.py 5 | @time:2019/8/125:04 PM 6 | """ 7 | from __future__ import absolute_import 8 | from __future__ import division 9 | from __future__ import print_function 10 | 11 | import tensorflow as tf 12 | import collections 13 | import six 14 | import re 15 | 16 | def assert_rank(tensor, expected_rank, name=None): 17 | """Raises an exception if the tensor rank is not of the expected rank. 18 | 19 | Args: 20 | tensor: A tf.Tensor to check the rank of. 21 | expected_rank: Python integer or list of integers, expected rank. 22 | name: Optional name of the tensor for the error message. 23 | 24 | Raises: 25 | ValueError: If the expected shape doesn't match the actual shape. 26 | """ 27 | if name is None: 28 | name = tensor.name 29 | 30 | expected_rank_dict = {} 31 | if isinstance(expected_rank, six.integer_types): 32 | expected_rank_dict[expected_rank] = True 33 | else: 34 | for x in expected_rank: 35 | expected_rank_dict[x] = True 36 | 37 | actual_rank = tensor.shape.ndims 38 | if actual_rank not in expected_rank_dict: 39 | scope_name = tf.get_variable_scope().name 40 | raise ValueError( 41 | "For the tensor `%s` in scope `%s`, the actual rank " 42 | "`%d` (shape = %s) is not equal to the expected rank `%s`" % 43 | (name, scope_name, actual_rank, str(tensor.shape), str(expected_rank))) 44 | 45 | 46 | def get_shape_list(tensor, expected_rank=None, name=None): 47 | """Returns a list of the shape of tensor, preferring static dimensions. 48 | 49 | Args: 50 | tensor: A tf.Tensor object to find the shape of. 51 | expected_rank: (optional) int. The expected rank of `tensor`. If this is 52 | specified and the `tensor` has a different rank, and exception will be 53 | thrown. 54 | name: Optional name of the tensor for the error message. 55 | 56 | Returns: 57 | A list of dimensions of the shape of tensor. All static dimensions will 58 | be returned as python integers, and dynamic dimensions will be returned 59 | as tf.Tensor scalars. 60 | """ 61 | if name is None: 62 | name = tensor.name 63 | 64 | if expected_rank is not None: 65 | assert_rank(tensor, expected_rank, name) 66 | 67 | shape = tensor.shape.as_list() 68 | 69 | non_static_indexes = [] 70 | for (index, dim) in enumerate(shape): 71 | if dim is None: 72 | non_static_indexes.append(index) 73 | 74 | if not non_static_indexes: 75 | return shape 76 | 77 | dyn_shape = tf.shape(tensor) 78 | for index in non_static_indexes: 79 | shape[index] = dyn_shape[index] 80 | return shape 81 | 82 | 83 | def get_learning_rate(learning_rate, hidden_size, learning_rate_warmup_steps): 84 | """calculate learning rate with linear warmup and rsqrt decay""" 85 | with tf.name_scope('learning_rate'): 86 | warmup_steps = tf.to_float(learning_rate_warmup_steps) 87 | step = tf.to_float(tf.train.get_or_create_global_step()) 88 | 89 | learning_rate *= (hidden_size ** -0.5) 90 | # Apply linear warmup 91 | learning_rate *= tf.minimum(1.0, step/warmup_steps) 92 | # Apply rsqrt decay 93 | learning_rate *= tf.rsqrt(tf.maximum(step, warmup_steps)) 94 | 95 | tf.identity(learning_rate, "learning_rate") 96 | return learning_rate 97 | 98 | 99 | def get_train_op_and_metrics(loss, params): 100 | """Generate training op and metrics to save in tensorboard""" 101 | with tf.variable_scope('get_train_op'): 102 | learning_rate = get_learning_rate( 103 | learning_rate=params['learning_rate'], 104 | hidden_size=params['hidden_size'], 105 | learning_rate_warmup_steps=params['learning_rate_warmup_steps'] 106 | ) 107 | # create optimizer , Use lazyAdamOptimizer from TF contrib,which is faster than the TF core Adam operation 108 | optimizer = tf.contrib.opt.LazyAdamOptimizer( 109 | learning_rate=learning_rate, 110 | beta1=params['optimizer_adam_beta1'], 111 | beta2=params['optimizer_adam_beta2'], 112 | epsilon=params['optimizer_adam_epsilon'] 113 | ) 114 | # calculate and apply graph gradient using LazyAdamOptimizer 115 | global_step = tf.train.get_global_step() 116 | tvars = tf.trainable_variables() 117 | gradients = optimizer.compute_gradients(loss, tvars, colocate_gradients_with_ops=True) 118 | minimize_op = optimizer.apply_gradients(gradients, 119 | global_step=global_step, 120 | name='train') 121 | update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) 122 | train_op = tf.group(minimize_op, update_ops) 123 | 124 | train_metrics = {'learning_rate': learning_rate, 125 | 'global_step': global_step} 126 | return train_op, train_metrics 127 | 128 | 129 | def record_scalars(metric_dict): 130 | for key, value in metric_dict.items(): 131 | print('records_scalars', key) 132 | if key == 'accuracy': 133 | tf.summary.scalar(name=key, tensor=value[1]) 134 | else: 135 | tf.summary.scalar(name=key, tensor=value) 136 | 137 | 138 | def get_assignment_map_from_checkpoint(tvars, init_checkpoint): 139 | """Compute the union of the current variables and checkpoint variables.""" 140 | assignment_map = {} 141 | initialized_variable_names = {} 142 | 143 | name_to_variable = collections.OrderedDict() 144 | for var in tvars: 145 | name = var.name 146 | m = re.match("^(.*):\\d+$", name) 147 | if m is not None: 148 | name = m.group(1) 149 | name_to_variable[name] = var 150 | 151 | init_vars = tf.train.list_variables(init_checkpoint) 152 | 153 | assignment_map = collections.OrderedDict() 154 | for x in init_vars: 155 | (name, var) = (x[0], x[1]) 156 | if name not in name_to_variable: 157 | continue 158 | assignment_map[name] = name 159 | initialized_variable_names[name] = 1 160 | initialized_variable_names[name + ":0"] = 1 161 | 162 | return (assignment_map, initialized_variable_names) 163 | 164 | 165 | def parse_exmp(serial_exmp): 166 | feats = tf.parse_single_example(serial_exmp, features={'inputs': tf.VarLenFeature(tf.float32), 167 | 'target_correct': tf.VarLenFeature(tf.int64), 168 | 'target_id': tf.VarLenFeature(tf.float32), 169 | 170 | 'correct': tf.VarLenFeature(tf.int64), 171 | 'ids': tf.VarLenFeature(tf.float32), 172 | 173 | 'seq_len': tf.FixedLenFeature([], tf.int64)}) 174 | inputs = tf.sparse_tensor_to_dense(feats['inputs']) # 使用VarLenFeature读入的是一个sparse_tensor,用该函数进行转换 175 | target_correct = tf.sparse_tensor_to_dense(feats['target_correct']) 176 | target_id = tf.sparse_tensor_to_dense(feats['target_id']) 177 | 178 | correct = tf.sparse_tensor_to_dense(feats['correct']) 179 | id = tf.sparse_tensor_to_dense(feats['ids']) 180 | 181 | inputs = tf.cast(inputs, tf.int32) 182 | target_correct = tf.cast(target_correct, tf.float32) 183 | target_id = tf.cast(target_id, tf.int32) 184 | 185 | correct = tf.cast(correct, tf.float32) 186 | id = tf.cast(id, tf.int32) 187 | 188 | seq_len = tf.cast(feats['seq_len'], tf.int32) 189 | 190 | return {'inputs': inputs, 191 | 'target_correct': target_correct, 192 | 'target_id': target_id, 193 | 'ids': id, 194 | 'correct': correct, 195 | 'seq_len': seq_len} 196 | 197 | 198 | def get_dataset(fname): 199 | dataset = tf.data.TFRecordDataset(fname) 200 | return dataset.map(parse_exmp) # use padded_batch method if padding needed 201 | -------------------------------------------------------------------------------- /code/DKT/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:__init__.py.py 5 | @time:2019/10/811:10 AM 6 | """ -------------------------------------------------------------------------------- /code/DKT/metrics.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:metrics.py 5 | @time:2019/8/122:57 PM 6 | """ 7 | import tensorflow as tf 8 | from model_params import * 9 | from model import DKT 10 | 11 | 12 | def dkt_loss(logits, target_correct, target_ids, seq_steps): 13 | """ 14 | calculate cross-entropy loss for dkt 15 | Args: 16 | logits: logits(no sigmoid func) with shape [batch_size, length, vocab_size] 17 | target_correct: targets with shape [batch_size, length] 18 | target_ids: 19 | seq_steps: 20 | 21 | Returns: 22 | cross-entropy loss 23 | 24 | """ 25 | with tf.name_scope('loss'): 26 | batch_size, length, vocab_size = tf.shape(logits)[0], tf.shape(logits)[1], tf.shape(logits)[2] 27 | flat_logits = tf.reshape(logits, [-1]) 28 | flat_target_correct = tf.reshape(target_correct, [-1]) 29 | 30 | flat_base_target_index = tf.range(batch_size * length) * vocab_size 31 | flat_bias_target_id = tf.reshape(target_ids, [-1]) 32 | flat_target_id = flat_base_target_index + flat_bias_target_id 33 | 34 | flat_target_logits = tf.gather(flat_logits, flat_target_id) 35 | mask = tf.reshape(tf.sequence_mask(seq_steps, maxlen=length), [-1]) 36 | # drop predict which user not react to 37 | flat_target_correct_mask = tf.boolean_mask(flat_target_correct, mask) 38 | flat_target_logits_mask = tf.boolean_mask(flat_target_logits, mask) 39 | 40 | loss = tf.reduce_mean(tf.nn.weighted_cross_entropy_with_logits( 41 | targets=flat_target_correct_mask, 42 | logits=flat_target_logits_mask, 43 | pos_weight=1 44 | )) 45 | return loss 46 | 47 | 48 | def padded_accuracy(logits, target, target_ids, seq_len): 49 | """ 50 | Percentage of times that predictions matches albels on non-0s 51 | Args: 52 | logits: Tensor with shape [batch_size, length, vocab_size] 53 | target: Tesnor wtth shape [batch_size, length] 54 | 55 | Returns: 56 | 57 | """ 58 | with tf.variable_scope('padded_accuracy', values=[logits, target, target_ids, seq_len]): 59 | batch_size, length, vocab_size = tf.shape(logits)[0], tf.shape(logits)[1], tf.shape(logits)[2] 60 | flat_logits = tf.reshape(logits, [-1]) 61 | flat_target_correct = tf.reshape(target, [-1]) 62 | 63 | flat_base_target_index = tf.range(batch_size * length) * vocab_size 64 | flat_bias_target_id = tf.reshape(target_ids, [-1]) 65 | flat_target_id = flat_base_target_index + flat_bias_target_id 66 | 67 | flat_target_logits = tf.gather(flat_logits, flat_target_id) 68 | pred = tf.sigmoid(tf.reshape(flat_target_logits, [batch_size, length])) 69 | # self.binary_pred = tf.cast(tf.greater_equal(self.pred, 0.5), tf.int32) 70 | binary_pred = tf.cast(tf.greater(pred, 0.5), tf.float32) 71 | 72 | predict = tf.reshape(binary_pred, [-1]) 73 | mask = tf.reshape(tf.sequence_mask(seq_len, maxlen=tf.shape(logits)[1]), [-1]) 74 | flat_predict_mask = tf.boolean_mask(predict, mask) 75 | flat_target_mask = tf.boolean_mask(flat_target_correct, mask) 76 | return tf.equal(flat_target_mask, flat_predict_mask) 77 | 78 | 79 | def _convert_to_eval_metric(metric_fn): 80 | """ 81 | warper a metric_fn that returns scores and weights as an eval metric fn 82 | The input metric_fn returns values for the current batch. 83 | The wrapper aggregates the return values collected over all of the batches evaluated 84 | Args: 85 | metric_fn: function that return socres for current batch's logits and targets 86 | 87 | Returns: 88 | function that aggregates the score and weights from metric_fn 89 | 90 | """ 91 | def problem_metric_fn(*args): 92 | """ 93 | return an aggregation of the metric_fn's returned values 94 | Args: 95 | *args: 96 | 97 | Returns: 98 | 99 | """ 100 | score = metric_fn(*args) 101 | return tf.metrics.mean(score) 102 | return problem_metric_fn 103 | 104 | 105 | def get_eval_metrics(logits, labels, target_ids, seq_len): 106 | """ 107 | return dictionary of model evaluation metrics 108 | Args: 109 | logits: 110 | labels: 111 | params: 112 | 113 | Returns: 114 | 115 | """ 116 | metrics = { 117 | 'accuracy': _convert_to_eval_metric(padded_accuracy)(logits, labels, target_ids, seq_len), 118 | } 119 | return metrics -------------------------------------------------------------------------------- /code/DKT/model.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:model.py 5 | @time:2019/10/83:13 PM 6 | """ 7 | from __future__ import absolute_import 8 | from __future__ import division 9 | from __future__ import print_function 10 | 11 | import tensorflow as tf 12 | 13 | 14 | class DKT(object): 15 | def __init__(self, params, train): 16 | """ 17 | Initialize layers to build DKT model 18 | Args: 19 | params: hyperparameter object defining layer size, dropout value etc 20 | train: boolean indicating whether the model is in training mode 21 | """ 22 | self.params = params 23 | self.train = train 24 | 25 | def __call__(self, batch_size, inputs, seq_len): 26 | initial_state = [] 27 | if self.params['use_one_hot']: 28 | self.embedding_output = tf.one_hot(inputs, depth=2*self.params['num_skills'] + 1) 29 | else: # Use Embedding 30 | embedding_table = tf.get_variable( 31 | name="word_embedding", 32 | shape=[2*self.params['num_skills']+1, self.params['hidden_size']], 33 | initializer=tf.truncated_normal_initializer(self.params['initializer_range'])) 34 | self.embedding_output = tf.nn.embedding_lookup(embedding_table, inputs) 35 | self.embedding_output = tf.contrib.layers.layer_norm(self.embedding_output, 36 | begin_norm_axis=-1, 37 | begin_params_axis=-1, scope='embedding_ln') 38 | self.embedding_output = tf.nn.dropout(self.embedding_output, keep_prob=self.params['keep_prob']) 39 | 40 | hidden_layers = [] 41 | final_hidden_size = self.params['hidden_size'] 42 | with tf.variable_scope('lstm_cell', initializer=tf.orthogonal_initializer()): 43 | for i in range(self.params['hidden_layer_num']): 44 | if self.params['use_LN']: 45 | hidden_layer = tf.contrib.rnn.LayerNormBasicLSTMCell(num_units=final_hidden_size, 46 | forget_bias=0.0, 47 | layer_norm=True, 48 | dropout_keep_prob=self.params['keep_prob']) 49 | else: 50 | hidden_layer = tf.contrib.rnn.BasicLSTMCell(num_units=final_hidden_size, forget_bias=0, 51 | state_is_tuple=True) 52 | # lstm_layer = tf.contrib.rnn.GRUCell(num_units=final_hidden_size, state_is_tuple=True) 53 | 54 | hidden_layer = tf.contrib.rnn.DropoutWrapper(cell=hidden_layer, 55 | output_keep_prob=self.params['keep_prob']) 56 | hidden_layers.append(hidden_layer) 57 | initial_state.append(hidden_layer.zero_state(batch_size, dtype=tf.float32)) 58 | self.hidden_cell = tf.contrib.rnn.MultiRNNCell(cells=hidden_layers, state_is_tuple=True) 59 | 60 | # dynamic rnn 61 | state_series, self.current_state = tf.nn.dynamic_rnn(cell=self.hidden_cell, 62 | inputs=self.embedding_output, 63 | sequence_length=seq_len, 64 | initial_state= tuple(initial_state)) 65 | 66 | output_w = tf.get_variable("weights", [final_hidden_size, self.params['num_skills']], 67 | initializer=tf.truncated_normal_initializer(self.params['initializer_range']), 68 | # regularizer=tf.contrib.layers.l2_regularizer(self.config.regularization_lambda) 69 | regularizer=None 70 | ) 71 | 72 | output_b = tf.get_variable("biases", [self.params['num_skills']], 73 | initializer=tf.zeros_initializer(), 74 | # regularizer=tf.contrib.layers.l2_regularizer(self.config.regularization_lambda) 75 | regularizer=None 76 | ) 77 | 78 | batch_size, max_len = tf.shape(inputs)[0], tf.shape(inputs)[1] 79 | self.state_series = tf.reshape(state_series, [batch_size * max_len, final_hidden_size]) 80 | 81 | logits = tf.matmul(self.state_series, output_w) + output_b 82 | logits = tf.reshape(logits, [batch_size, max_len, self.params['num_skills']]) 83 | # self.pred_all = tf.sigmoid(self.mat_logits) 84 | 85 | return logits -------------------------------------------------------------------------------- /code/DKT/model_params.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:model_params.py 5 | @time:2019/10/83:31 PM 6 | """ 7 | from collections import defaultdict 8 | 9 | 10 | BASE_PARAMS = defaultdict( 11 | # Input params 12 | num_skills=125, 13 | 14 | # Model params 15 | initializer_range=0.02, # embedding table initializer 16 | keep_prob=0.5, 17 | use_LN=True, 18 | hidden_layer_num=4, 19 | hidden_size=128, 20 | use_one_hot=False, 21 | 22 | # Training params 23 | learning_rate=0.1, 24 | learning_rate_decay_rate=1.0, 25 | learning_rate_warmup_steps=16000, 26 | 27 | # Optimizer params 28 | optimizer_adam_beta1=0.9, 29 | optimizer_adam_beta2=0.997, 30 | optimizer_adam_epsilon=1e-09, 31 | 32 | ) -------------------------------------------------------------------------------- /code/DKT/run_dkt.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:run_dkt.py 5 | @time:2019/8/1212:39 PM 6 | """ 7 | from __future__ import absolute_import 8 | from __future__ import division 9 | from __future__ import print_function 10 | 11 | import tensorflow as tf 12 | import os 13 | import numpy as np 14 | from model_params import BASE_PARAMS 15 | from model import * 16 | from metrics import * 17 | from utils import * 18 | 19 | 20 | 21 | flags = tf.flags 22 | logging = tf.logging 23 | 24 | flags.DEFINE_string(name='train_data', default='../data/train.tfrecord', 25 | help='Path of training data ') 26 | flags.DEFINE_string(name='valid_data', default='../data/eval.tfrecord', 27 | help='Path of valid data') 28 | flags.DEFINE_string(name='predict_data', default='', 29 | help='Path of predict data') 30 | flags.DEFINE_string(name='saved_model', default='./saved_model', help='Path to save model') 31 | flags.DEFINE_string(name='output_dir', default='./output_dir', help='Path to save model') 32 | flags.DEFINE_integer(name='epoch', default=100, help='Num of epoch') 33 | flags.DEFINE_integer(name='batch_size', default=1, help='Num of batch') 34 | flags.DEFINE_bool(name="do_train", default=True, help="Whether to run training.") 35 | flags.DEFINE_bool(name="do_eval", default=True, help="Whether to run eval on the dev set.") 36 | flags.DEFINE_bool( 37 | "do_predict", False, 38 | "Whether to run the model in inference mode on the test set.") 39 | flags.DEFINE_bool(name='pb', default=True, help='') 40 | FLAGS = flags.FLAGS 41 | 42 | 43 | def model_fn_builder(): 44 | def model_fn(features, labels, mode, params): 45 | """ 46 | define how to train, evaluate and predict from the transfomer model. 47 | Args: 48 | 49 | mode: 50 | params: 51 | 52 | Returns: 53 | 54 | """ 55 | inputs = features['inputs'] 56 | seq_steps = features['seq_len'] 57 | 58 | 59 | is_training = (mode == tf.estimator.ModeKeys.TRAIN) 60 | 61 | try: 62 | batch_size, length = get_shape_list(inputs, expected_rank=2) 63 | except ValueError: 64 | batch_size = 1 65 | length = get_shape_list(inputs, expected_rank=1)[0] 66 | inputs = tf.reshape(inputs, [batch_size, length]) 67 | 68 | with tf.variable_scope('model'): 69 | # Build model 70 | model = DKT(params, is_training) 71 | logits = model(batch_size, inputs, seq_steps) # [batch, length, vocab_size] 72 | 73 | # when in prediction mode, the label/target is Bone, the model output is the prediction 74 | if mode == tf.estimator.ModeKeys.PREDICT: 75 | export_outputs = {'predict_output': tf.estimator.export.PredictOutput({"predict": tf.sigmoid(logits)})} 76 | output_spec = tf.estimator.EstimatorSpec(mode=mode, 77 | predictions={'predict': tf.sigmoid(logits)}, 78 | export_outputs=export_outputs 79 | ) 80 | else: 81 | # Calculate model loss 82 | target_ids = features['target_id'] 83 | target_correct = features['target_correct'] 84 | 85 | loss = dkt_loss(logits, target_correct, target_ids, seq_steps) 86 | record_dict = {} 87 | record_dict['minibatch_loss'] = loss 88 | # Save loss as named tensor will be logged with the logging hook 89 | tf.identity(loss, 'cross_entropy') 90 | 91 | if mode == tf.estimator.ModeKeys.EVAL: 92 | metric_dict = get_eval_metrics(logits, target_correct, target_ids, seq_steps) 93 | record_dict['accuracy'] = metric_dict['accuracy'] 94 | record_scalars(record_dict) 95 | output_spec = tf.estimator.EstimatorSpec(mode=tf.estimator.ModeKeys.EVAL, 96 | loss=loss, 97 | predictions={'predict': tf.sigmoid(logits)}, 98 | eval_metric_ops=metric_dict ) 99 | else: # train 100 | # check whether restore from checkpoint 101 | tvars = tf.trainable_variables() 102 | initialized_variable_names = {} 103 | 104 | tf.logging.info("**** Trainable Variables ****") 105 | for var in tvars: 106 | init_string = "" 107 | if var.name in initialized_variable_names: 108 | init_string = ", *INIT_FROM_CKPT*" 109 | tf.logging.info(" name = %s, shape = %s%s", var.name, var.shape, 110 | init_string) 111 | 112 | train_op, metric_dict = get_train_op_and_metrics(loss, params) 113 | acc_metric = get_eval_metrics(logits, target_correct, target_ids, seq_steps) 114 | record_dict['accuracy'] = acc_metric['accuracy'] 115 | record_dict['learning_rate'] = metric_dict['learning_rate'] 116 | record_scalars(record_dict) 117 | output_spec = tf.estimator.EstimatorSpec(mode=tf.estimator.ModeKeys.TRAIN, 118 | loss=loss, 119 | train_op=train_op 120 | ) 121 | return output_spec 122 | return model_fn 123 | 124 | 125 | def input_fn_builder(datadir, epoch): 126 | def input_fn(): 127 | padded_shapes = {'inputs': [None], 128 | 'target_correct': [None], 129 | 'target_id': [None], 130 | 'ids': [None], 131 | 'correct': [None], 132 | 133 | 'seq_len': [], 134 | } 135 | 136 | dataset_train = get_dataset(datadir) 137 | dataset_train = dataset_train.repeat(epoch).shuffle(1000).padded_batch(FLAGS.batch_size, 138 | padded_shapes=padded_shapes) 139 | 140 | return dataset_train 141 | 142 | return input_fn 143 | 144 | 145 | def main(): 146 | # if not FLAGS.do_train and not FLAGS.do_eval and not FLAGS.do_predict: 147 | # raise ValueError("At least one of `do_train`, `do_eval` or `do_predict' must be True.") 148 | params = BASE_PARAMS # param dict from model_params.py 149 | tf.gfile.MakeDirs(FLAGS.saved_model) 150 | 151 | run_config = tf.estimator.RunConfig( 152 | model_dir=FLAGS.saved_model, 153 | tf_random_seed=tf.set_random_seed([42]), 154 | # save_summary_step=100, 155 | save_checkpoints_steps=1000, 156 | # save_checkpoints_secs=600, 157 | session_config=None, 158 | keep_checkpoint_max=5, 159 | keep_checkpoint_every_n_hours=10000, 160 | log_step_count_steps=100, 161 | train_distribute=None 162 | ) 163 | model_fn = model_fn_builder() 164 | estimator = tf.estimator.Estimator( 165 | model_fn=model_fn, 166 | model_dir=FLAGS.saved_model, 167 | config=run_config, 168 | params=params, 169 | warm_start_from=None 170 | ) 171 | # train_input_fn = input_fn_builder(FLAGS.train_data, FLAGS.epoch) 172 | # train_spec = tf.estimator.TrainSpec(input_fn=train_input_fn, 173 | # max_steps=None) 174 | # eval_input_fn = input_fn_builder(FLAGS.valid_data, 1) 175 | # eval_spec = tf.estimator.EvalSpec(input_fn=eval_input_fn, 176 | # steps=None, 177 | # throttle_secs=60) 178 | # tf.estimator.train_and_evaluate(estimator, 179 | # train_spec=train_spec, 180 | # eval_spec=eval_spec) 181 | for epoch in range(FLAGS.epoch): 182 | if FLAGS.do_train: 183 | train_input_fn = input_fn_builder(FLAGS.train_data, 1) 184 | 185 | estimator.train(input_fn=train_input_fn) 186 | if FLAGS.do_eval: 187 | eval_input_fn = input_fn_builder(FLAGS.valid_data, 1) 188 | result = estimator.evaluate(input_fn=eval_input_fn) 189 | 190 | output_eval_file = os.path.join(FLAGS.output_dir, "eval_results.txt") 191 | with tf.gfile.GFile(output_eval_file, "w+") as writer: 192 | tf.logging.info("***** Eval results *****") 193 | for key in sorted(result.keys()): 194 | tf.logging.info(" %s = %s", key, str(result[key])) 195 | writer.write("%s = %s\n" % (key, str(result[key]))) 196 | 197 | 198 | if __name__ == '__main__': 199 | tf.logging.set_verbosity(tf.logging.INFO) 200 | # flags.mark_flag_as_required("train_data") 201 | # flags.mark_flag_as_required("valid_data") 202 | # flags.mark_flag_as_required("saved_model") 203 | 204 | main() 205 | -------------------------------------------------------------------------------- /code/DKT/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:utils.py 5 | @time:2019/8/125:04 PM 6 | """ 7 | from __future__ import absolute_import 8 | from __future__ import division 9 | from __future__ import print_function 10 | 11 | import tensorflow as tf 12 | import collections 13 | import six 14 | import re 15 | 16 | def assert_rank(tensor, expected_rank, name=None): 17 | """Raises an exception if the tensor rank is not of the expected rank. 18 | 19 | Args: 20 | tensor: A tf.Tensor to check the rank of. 21 | expected_rank: Python integer or list of integers, expected rank. 22 | name: Optional name of the tensor for the error message. 23 | 24 | Raises: 25 | ValueError: If the expected shape doesn't match the actual shape. 26 | """ 27 | if name is None: 28 | name = tensor.name 29 | 30 | expected_rank_dict = {} 31 | if isinstance(expected_rank, six.integer_types): 32 | expected_rank_dict[expected_rank] = True 33 | else: 34 | for x in expected_rank: 35 | expected_rank_dict[x] = True 36 | 37 | actual_rank = tensor.shape.ndims 38 | if actual_rank not in expected_rank_dict: 39 | scope_name = tf.get_variable_scope().name 40 | raise ValueError( 41 | "For the tensor `%s` in scope `%s`, the actual rank " 42 | "`%d` (shape = %s) is not equal to the expected rank `%s`" % 43 | (name, scope_name, actual_rank, str(tensor.shape), str(expected_rank))) 44 | 45 | 46 | def get_shape_list(tensor, expected_rank=None, name=None): 47 | """Returns a list of the shape of tensor, preferring static dimensions. 48 | 49 | Args: 50 | tensor: A tf.Tensor object to find the shape of. 51 | expected_rank: (optional) int. The expected rank of `tensor`. If this is 52 | specified and the `tensor` has a different rank, and exception will be 53 | thrown. 54 | name: Optional name of the tensor for the error message. 55 | 56 | Returns: 57 | A list of dimensions of the shape of tensor. All static dimensions will 58 | be returned as python integers, and dynamic dimensions will be returned 59 | as tf.Tensor scalars. 60 | """ 61 | if name is None: 62 | name = tensor.name 63 | 64 | if expected_rank is not None: 65 | assert_rank(tensor, expected_rank, name) 66 | 67 | shape = tensor.shape.as_list() 68 | 69 | non_static_indexes = [] 70 | for (index, dim) in enumerate(shape): 71 | if dim is None: 72 | non_static_indexes.append(index) 73 | 74 | if not non_static_indexes: 75 | return shape 76 | 77 | dyn_shape = tf.shape(tensor) 78 | for index in non_static_indexes: 79 | shape[index] = dyn_shape[index] 80 | return shape 81 | 82 | 83 | def get_learning_rate(learning_rate, hidden_size, learning_rate_warmup_steps): 84 | """calculate learning rate with linear warmup and rsqrt decay""" 85 | with tf.name_scope('learning_rate'): 86 | warmup_steps = tf.to_float(learning_rate_warmup_steps) 87 | step = tf.to_float(tf.train.get_or_create_global_step()) 88 | 89 | learning_rate *= (hidden_size ** -0.5) 90 | # Apply linear warmup 91 | learning_rate *= tf.minimum(1.0, step/warmup_steps) 92 | # Apply rsqrt decay 93 | learning_rate *= tf.rsqrt(tf.maximum(step, warmup_steps)) 94 | 95 | tf.identity(learning_rate, "learning_rate") 96 | return learning_rate 97 | 98 | 99 | def get_train_op_and_metrics(loss, params): 100 | """Generate training op and metrics to save in tensorboard""" 101 | with tf.variable_scope('get_train_op'): 102 | learning_rate = get_learning_rate( 103 | learning_rate=params['learning_rate'], 104 | hidden_size=params['hidden_size'], 105 | learning_rate_warmup_steps=params['learning_rate_warmup_steps'] 106 | ) 107 | # create optimizer , Use lazyAdamOptimizer from TF contrib,which is faster than the TF core Adam operation 108 | optimizer = tf.contrib.opt.LazyAdamOptimizer( 109 | learning_rate=learning_rate, 110 | beta1=params['optimizer_adam_beta1'], 111 | beta2=params['optimizer_adam_beta2'], 112 | epsilon=params['optimizer_adam_epsilon'] 113 | ) 114 | # calculate and apply graph gradient using LazyAdamOptimizer 115 | global_step = tf.train.get_global_step() 116 | tvars = tf.trainable_variables() 117 | gradients = optimizer.compute_gradients(loss, tvars, colocate_gradients_with_ops=True) 118 | minimize_op = optimizer.apply_gradients(gradients, 119 | global_step=global_step, 120 | name='train') 121 | update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) 122 | train_op = tf.group(minimize_op, update_ops) 123 | 124 | train_metrics = {'learning_rate': learning_rate, 125 | 'global_step': global_step} 126 | return train_op, train_metrics 127 | 128 | 129 | def record_scalars(metric_dict): 130 | for key, value in metric_dict.items(): 131 | print('records_scalars', key) 132 | if key == 'accuracy': 133 | tf.summary.scalar(name=key, tensor=value[1]) 134 | else: 135 | tf.summary.scalar(name=key, tensor=value) 136 | 137 | 138 | def get_assignment_map_from_checkpoint(tvars, init_checkpoint): 139 | """Compute the union of the current variables and checkpoint variables.""" 140 | assignment_map = {} 141 | initialized_variable_names = {} 142 | 143 | name_to_variable = collections.OrderedDict() 144 | for var in tvars: 145 | name = var.name 146 | m = re.match("^(.*):\\d+$", name) 147 | if m is not None: 148 | name = m.group(1) 149 | name_to_variable[name] = var 150 | 151 | init_vars = tf.train.list_variables(init_checkpoint) 152 | 153 | assignment_map = collections.OrderedDict() 154 | for x in init_vars: 155 | (name, var) = (x[0], x[1]) 156 | if name not in name_to_variable: 157 | continue 158 | assignment_map[name] = name 159 | initialized_variable_names[name] = 1 160 | initialized_variable_names[name + ":0"] = 1 161 | 162 | return (assignment_map, initialized_variable_names) 163 | 164 | 165 | def parse_exmp(serial_exmp): 166 | feats = tf.parse_single_example(serial_exmp, features={'inputs': tf.VarLenFeature(tf.float32), 167 | 'target_correct': tf.VarLenFeature(tf.int64), 168 | 'target_id': tf.VarLenFeature(tf.float32), 169 | 170 | 'correct': tf.VarLenFeature(tf.int64), 171 | 'id': tf.VarLenFeature(tf.float32), 172 | 173 | 'seq_len': tf.FixedLenFeature([], tf.int64)}) 174 | inputs = tf.sparse_tensor_to_dense(feats['inputs']) # 使用VarLenFeature读入的是一个sparse_tensor,用该函数进行转换 175 | target_correct = tf.sparse_tensor_to_dense(feats['target_correct']) 176 | target_id = tf.sparse_tensor_to_dense(feats['target_id']) 177 | 178 | correct = tf.sparse_tensor_to_dense(feats['correct']) 179 | id = tf.sparse_tensor_to_dense(feats['id']) 180 | 181 | inputs = tf.cast(inputs, tf.int32) 182 | target_correct = tf.cast(target_correct, tf.float32) 183 | target_id = tf.cast(target_id, tf.int32) 184 | 185 | correct = tf.cast(correct, tf.float32) 186 | id = tf.cast(id, tf.int32) 187 | 188 | seq_len = tf.cast(feats['seq_len'], tf.int32) 189 | 190 | return {'inputs': inputs, 191 | 'target_correct': target_correct, 192 | 'target_id': target_id, 193 | 'ids': id, 194 | 'correct': correct, 195 | 'seq_len': seq_len} 196 | 197 | 198 | def get_dataset(fname): 199 | dataset = tf.data.TFRecordDataset(fname) 200 | return dataset.map(parse_exmp) # use padded_batch method if padding needed 201 | -------------------------------------------------------------------------------- /code/TCN-KT/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:__init__.py.py 5 | @time:2019/10/83:01 PM 6 | """ -------------------------------------------------------------------------------- /code/TCN-KT/metrics.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:metrics.py 5 | @time:2019/8/122:57 PM 6 | """ 7 | import tensorflow as tf 8 | from model_params import * 9 | from model import DKT 10 | 11 | 12 | def dkt_loss(logits, target_correct, target_ids, seq_steps): 13 | """ 14 | calculate cross-entropy loss for dkt 15 | Args: 16 | logits: logits(no sigmoid func) with shape [batch_size, length, vocab_size] 17 | target_correct: targets with shape [batch_size, length] 18 | target_ids: 19 | seq_steps: 20 | 21 | Returns: 22 | cross-entropy loss 23 | 24 | """ 25 | with tf.name_scope('loss'): 26 | batch_size, length, vocab_size = tf.shape(logits)[0], tf.shape(logits)[1], tf.shape(logits)[2] 27 | flat_logits = tf.reshape(logits, [-1]) 28 | flat_target_correct = tf.reshape(target_correct, [-1]) 29 | 30 | flat_base_target_index = tf.range(batch_size * length) * vocab_size 31 | flat_bias_target_id = tf.reshape(target_ids, [-1]) 32 | flat_target_id = flat_base_target_index + flat_bias_target_id 33 | 34 | flat_target_logits = tf.gather(flat_logits, flat_target_id) 35 | mask = tf.reshape(tf.sequence_mask(seq_steps, maxlen=length), [-1]) 36 | # drop predict which user not react to 37 | flat_target_correct_mask = tf.boolean_mask(flat_target_correct, mask) 38 | flat_target_logits_mask = tf.boolean_mask(flat_target_logits, mask) 39 | 40 | loss = tf.reduce_mean(tf.nn.weighted_cross_entropy_with_logits( 41 | targets=flat_target_correct_mask, 42 | logits=flat_target_logits_mask, 43 | pos_weight=1 44 | )) 45 | return loss 46 | 47 | 48 | def padded_accuracy(logits, target, target_ids, seq_len): 49 | """ 50 | Percentage of times that predictions matches albels on non-0s 51 | Args: 52 | logits: Tensor with shape [batch_size, length, vocab_size] 53 | target: Tesnor wtth shape [batch_size, length] 54 | 55 | Returns: 56 | 57 | """ 58 | with tf.variable_scope('padded_accuracy', values=[logits, target, target_ids, seq_len]): 59 | batch_size, length, vocab_size = tf.shape(logits)[0], tf.shape(logits)[1], tf.shape(logits)[2] 60 | flat_logits = tf.reshape(logits, [-1]) 61 | flat_target_correct = tf.reshape(target, [-1]) 62 | 63 | flat_base_target_index = tf.range(batch_size * length) * vocab_size 64 | flat_bias_target_id = tf.reshape(target_ids, [-1]) 65 | flat_target_id = flat_base_target_index + flat_bias_target_id 66 | 67 | flat_target_logits = tf.gather(flat_logits, flat_target_id) 68 | pred = tf.sigmoid(tf.reshape(flat_target_logits, [batch_size, length])) 69 | # self.binary_pred = tf.cast(tf.greater_equal(self.pred, 0.5), tf.int32) 70 | binary_pred = tf.cast(tf.greater(pred, 0.5), tf.float32) 71 | 72 | predict = tf.reshape(binary_pred, [-1]) 73 | mask = tf.reshape(tf.sequence_mask(seq_len, maxlen=tf.shape(logits)[1]), [-1]) 74 | flat_predict_mask = tf.boolean_mask(predict, mask) 75 | flat_target_mask = tf.boolean_mask(flat_target_correct, mask) 76 | return tf.equal(flat_target_mask, flat_predict_mask) 77 | 78 | 79 | def _convert_to_eval_metric(metric_fn): 80 | """ 81 | warper a metric_fn that returns scores and weights as an eval metric fn 82 | The input metric_fn returns values for the current batch. 83 | The wrapper aggregates the return values collected over all of the batches evaluated 84 | Args: 85 | metric_fn: function that return socres for current batch's logits and targets 86 | 87 | Returns: 88 | function that aggregates the score and weights from metric_fn 89 | 90 | """ 91 | def problem_metric_fn(*args): 92 | """ 93 | return an aggregation of the metric_fn's returned values 94 | Args: 95 | *args: 96 | 97 | Returns: 98 | 99 | """ 100 | score = metric_fn(*args) 101 | return tf.metrics.mean(score) 102 | return problem_metric_fn 103 | 104 | 105 | def get_eval_metrics(logits, labels, target_ids, seq_len): 106 | """ 107 | return dictionary of model evaluation metrics 108 | Args: 109 | logits: 110 | labels: 111 | params: 112 | 113 | Returns: 114 | 115 | """ 116 | metrics = { 117 | 'accuracy': _convert_to_eval_metric(padded_accuracy)(logits, labels, target_ids, seq_len), 118 | } 119 | return metrics -------------------------------------------------------------------------------- /code/TCN-KT/model.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:model.py 5 | @time:2019/10/86:24 PM 6 | """ 7 | from __future__ import absolute_import 8 | from __future__ import division 9 | from __future__ import print_function 10 | 11 | import tensorflow as tf 12 | from modules import TemporalConvNet 13 | 14 | 15 | class DKT(object): 16 | def __init__(self, params, train): 17 | """ 18 | Initialize layers to build DKT model 19 | Args: 20 | params: hyperparameter object defining layer size, dropout value etc 21 | train: boolean indicating whether the model is in training mode 22 | """ 23 | self.params = params 24 | self.train = train 25 | num_channels = [self.params['hidden_size'] * self.params['hidden_layer_num']] 26 | self.tcn = TemporalConvNet(num_channels, 27 | stride=1, 28 | kernel_size=self.params['kernel_size'], 29 | dropout=1-self.params['keep_prob']) 30 | 31 | def __call__(self, inputs): 32 | 33 | if self.params['use_one_hot']: 34 | self.embedding_output = tf.one_hot(inputs, depth=2*self.params['num_skills'] + 1) 35 | else: # Use Embedding 36 | embedding_table = tf.get_variable( 37 | name="word_embedding", 38 | shape=[2*self.params['num_skills']+1, self.params['hidden_size']], 39 | initializer=tf.truncated_normal_initializer(self.params['initializer_range'])) 40 | self.embedding_output = tf.nn.embedding_lookup(embedding_table, inputs) 41 | self.embedding_output = tf.contrib.layers.layer_norm(self.embedding_output, 42 | begin_norm_axis=-1, 43 | begin_params_axis=-1, scope='embedding_ln') 44 | self.embedding_output = tf.nn.dropout(self.embedding_output, keep_prob=self.params['keep_prob']) 45 | tcn_output = self.tcn(self.embedding_output) 46 | logits = tf.layers.dense(tcn_output, self.params['num_skills'], activation=None, use_bias=True) 47 | return logits -------------------------------------------------------------------------------- /code/TCN-KT/model_params.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:model_params.py 5 | @time:2019/10/83:31 PM 6 | """ 7 | from collections import defaultdict 8 | 9 | 10 | BASE_PARAMS = defaultdict( 11 | # Input params 12 | num_skills=125, 13 | 14 | # Model params 15 | initializer_range=0.02, # embedding table initializer 16 | keep_prob=0.5, 17 | use_LN=True, 18 | hidden_layer_num=4, 19 | hidden_size=128, 20 | kernel_size = 3, # 卷积核大小 21 | use_one_hot=False, 22 | 23 | # Training params 24 | learning_rate=0.1, 25 | learning_rate_decay_rate=1.0, 26 | learning_rate_warmup_steps=16000, 27 | 28 | # Optimizer params 29 | optimizer_adam_beta1=0.9, 30 | optimizer_adam_beta2=0.997, 31 | optimizer_adam_epsilon=1e-09, 32 | 33 | ) -------------------------------------------------------------------------------- /code/TCN-KT/modules.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:modules.py 5 | @time:2019/10/86:25 PM 6 | """ 7 | import tensorflow as tf 8 | from tensorflow.python.framework import tensor_shape 9 | from tensorflow.python.layers import base 10 | from tensorflow.python.layers import utils 11 | from tensorflow.python.layers import convolutional as convolutional_layers 12 | from tensorflow.python.ops import init_ops 13 | from tensorflow.python.ops import nn_ops 14 | from tensorflow.python.ops import nn_impl 15 | 16 | 17 | class TemporalConvNet(object): 18 | def __init__(self, num_channels, stride=1, kernel_size=2, dropout=0.2): 19 | self.kernel_size = kernel_size 20 | self.stride = stride 21 | self.num_levels = len(num_channels) 22 | self.num_channels = num_channels 23 | self.dropout = dropout 24 | 25 | def __call__(self, inputs): 26 | inputs_shape = inputs.get_shape().as_list() 27 | outputs = [inputs] 28 | for i in range(self.num_levels): 29 | dilation_size = 2 ** i 30 | in_channels = inputs_shape[-1] if i == 0 else self.num_channels[i - 1] 31 | out_channels = self.num_channels[i] 32 | output = self._TemporalBlock(outputs[-1], in_channels, out_channels, self.kernel_size, 33 | self.stride, dilation=dilation_size, 34 | padding=(self.kernel_size - 1) * dilation_size, 35 | dropout=self.dropout, level=i) 36 | outputs.append(output) 37 | # (batch, max_len, embedding_size) 38 | return outputs[-1] 39 | 40 | def _TemporalBlock(self, value, n_inputs, n_outputs, kernel_size, stride, dilation, padding, dropout=0.2, level=0): 41 | padded_value1 = tf.pad(value, [[0, 0], [padding, 0], [0, 0]]) 42 | # 定义第一个空洞卷积层 43 | self.conv1 = wnconv1d(inputs=padded_value1, 44 | filters=n_outputs, 45 | kernel_size=kernel_size, 46 | strides=stride, 47 | padding='valid', 48 | dilation_rate=dilation, 49 | activation=None, 50 | weight_norm=True, # default is false. 51 | kernel_initializer=tf.random_normal_initializer(0, 0.01), 52 | bias_initializer=tf.zeros_initializer(), 53 | name='layer' + str(level) + '_conv1') 54 | # 添加激活函数与dropout正则化方法完成第一个卷积 55 | self.output1 = tf.nn.dropout(tf.nn.relu(self.conv1), keep_prob=1 - dropout) 56 | 57 | # 堆叠同样结构的第二个卷积层 58 | padded_value2 = tf.pad(self.output1, [[0, 0], [padding, 0], [0, 0]]) 59 | self.conv2 = wnconv1d(inputs=padded_value2, 60 | filters=n_outputs, 61 | kernel_size=kernel_size, 62 | strides=stride, 63 | padding='valid', 64 | dilation_rate=dilation, 65 | activation=None, 66 | weight_norm=False, # default is False. 67 | kernel_initializer=tf.random_normal_initializer(0, 0.01), 68 | bias_initializer=tf.zeros_initializer(), 69 | name='layer' + str(level) + '_conv2') 70 | self.output2 = tf.nn.dropout(tf.nn.relu(self.conv2), keep_prob=1 - dropout) 71 | # 如果通道数不一样,那么需要对输入x做一个逐元素的一维卷积以使得它的纬度与前面两个卷积相等 72 | if n_inputs != n_outputs: 73 | res_x = tf.layers.conv1d(inputs=value, 74 | filters=n_outputs, 75 | kernel_size=1, 76 | activation=None, 77 | kernel_initializer=tf.random_normal_initializer(0, 0.01), 78 | bias_initializer=tf.zeros_initializer(), 79 | name='layer' + str(level) + '_conv') 80 | else: 81 | res_x = value 82 | return tf.nn.relu(res_x + self.output2) 83 | 84 | 85 | class _WNConv(convolutional_layers._Conv): 86 | def __init__(self, *args, **kwargs): 87 | self.weight_norm = kwargs.pop('weight_norm') 88 | super(_WNConv, self).__init__(*args, **kwargs) 89 | 90 | def build(self, input_shape): 91 | input_shape = tensor_shape.TensorShape(input_shape) 92 | if self.data_format == 'channels_first': 93 | channel_axis = 1 94 | else: 95 | channel_axis = -1 96 | 97 | if input_shape[channel_axis].value is None: 98 | raise ValueError('The channel dimension of the inputs ' 99 | 'should be defined. Found `None`.') 100 | input_dim = input_shape[channel_axis].value 101 | # kernel_shape=(self.kernel_size, input_dim, self.filters) 102 | kernel_shape = self.kernel_size + (input_dim, self.filters) 103 | 104 | kernel = self.add_variable(name='kernel', 105 | shape=kernel_shape, 106 | initializer=self.kernel_initializer, 107 | regularizer=self.kernel_regularizer, 108 | constraint=self.kernel_constraint, 109 | trainable=True, 110 | dtype=self.dtype) 111 | # weight normalization 112 | if self.weight_norm: 113 | g = self.add_variable(name='wn/g', 114 | shape=(self.filters,), 115 | initializer=init_ops.ones_initializer(), 116 | dtype=kernel.dtype, 117 | trainable=True) 118 | self.kernel = tf.reshape(g, [1, 1, self.filters]) * nn_impl.l2_normalize(kernel, [0, 1]) 119 | else: 120 | self.kernel = kernel 121 | 122 | if self.use_bias: 123 | self.bias = self.add_variable(name='bias', 124 | shape=(self.filters,), 125 | initializer=self.bias_initializer, 126 | regularizer=self.bias_regularizer, 127 | constraint=self.bias_constraint, 128 | trainable=True, 129 | dtype=self.dtype) 130 | else: 131 | self.bias = None 132 | self.input_spec = base.InputSpec(ndim=self.rank + 2, 133 | axes={channel_axis: input_dim}) 134 | self._convolution_op = nn_ops.Convolution( 135 | input_shape, 136 | filter_shape=self.kernel.get_shape(), 137 | dilation_rate=self.dilation_rate, 138 | strides=self.strides, 139 | padding=self.padding.upper(), 140 | data_format=utils.convert_data_format(self.data_format, 141 | self.rank + 2)) 142 | self.built = True 143 | 144 | 145 | class WNConv1D(_WNConv): 146 | def __init__(self, filters, 147 | kernel_size, 148 | strides=1, 149 | padding='valid', 150 | data_format='channels_last', 151 | dilation_rate=1, 152 | activation=None, 153 | weight_norm=False, 154 | use_bias=True, 155 | kernel_initializer=None, 156 | bias_initializer=init_ops.zeros_initializer(), 157 | kernel_regularizer=None, 158 | bias_regularizer=None, 159 | activity_regularizer=None, 160 | kernel_constraint=None, 161 | bias_constraint=None, 162 | trainable=True, 163 | name=None, 164 | **kwargs): 165 | super(WNConv1D, self).__init__( 166 | rank=1, 167 | filters=filters, 168 | kernel_size=kernel_size, 169 | strides=strides, 170 | padding=padding, 171 | data_format=data_format, 172 | dilation_rate=dilation_rate, 173 | activation=activation, 174 | weight_norm=weight_norm, 175 | use_bias=use_bias, 176 | kernel_initializer=kernel_initializer, 177 | bias_initializer=bias_initializer, 178 | kernel_regularizer=kernel_regularizer, 179 | bias_regularizer=bias_regularizer, 180 | activity_regularizer=activity_regularizer, 181 | kernel_constraint=kernel_constraint, 182 | bias_constraint=bias_constraint, 183 | trainable=trainable, 184 | name=name, **kwargs) 185 | 186 | 187 | def wnconv1d(inputs, 188 | filters, 189 | kernel_size, 190 | strides=1, 191 | padding='valid', 192 | data_format='channels_last', 193 | dilation_rate=1, 194 | activation=None, 195 | weight_norm=True, 196 | use_bias=True, 197 | kernel_initializer=None, 198 | bias_initializer=init_ops.zeros_initializer(), 199 | kernel_regularizer=None, 200 | bias_regularizer=None, 201 | activity_regularizer=None, 202 | kernel_constraint=None, 203 | bias_constraint=None, 204 | trainable=True, 205 | name=None, 206 | reuse=None): 207 | layer = WNConv1D( 208 | filters=filters, 209 | kernel_size=kernel_size, 210 | strides=strides, 211 | padding=padding, 212 | data_format=data_format, 213 | dilation_rate=dilation_rate, 214 | activation=activation, 215 | weight_norm=weight_norm, 216 | use_bias=use_bias, 217 | kernel_initializer=kernel_initializer, 218 | bias_initializer=bias_initializer, 219 | kernel_regularizer=kernel_regularizer, 220 | bias_regularizer=bias_regularizer, 221 | activity_regularizer=activity_regularizer, 222 | kernel_constraint=kernel_constraint, 223 | bias_constraint=bias_constraint, 224 | trainable=trainable, 225 | name=name, 226 | dtype=inputs.dtype.base_dtype, 227 | _reuse=reuse, 228 | _scope=name) 229 | 230 | return layer.apply(inputs) 231 | -------------------------------------------------------------------------------- /code/TCN-KT/run_dkt.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:run_dkt.py 5 | @time:2019/8/1212:39 PM 6 | """ 7 | from __future__ import absolute_import 8 | from __future__ import division 9 | from __future__ import print_function 10 | 11 | import tensorflow as tf 12 | import os 13 | import numpy as np 14 | from model_params import BASE_PARAMS 15 | from model import * 16 | from metrics import * 17 | from utils import * 18 | 19 | 20 | 21 | flags = tf.flags 22 | logging = tf.logging 23 | 24 | flags.DEFINE_string(name='train_data', default='../data/train.tfrecord', 25 | help='Path of training data ') 26 | flags.DEFINE_string(name='valid_data', default='../data/eval.tfrecord', 27 | help='Path of valid data') 28 | flags.DEFINE_string(name='predict_data', default='', 29 | help='Path of predict data') 30 | flags.DEFINE_string(name='saved_model', default='./saved_model', help='Path to save model') 31 | flags.DEFINE_string(name='output_dir', default='./output_dir', help='Path to save model') 32 | flags.DEFINE_integer(name='epoch', default=100, help='Num of epoch') 33 | flags.DEFINE_integer(name='batch_size', default=1, help='Num of batch') 34 | flags.DEFINE_bool(name="do_train", default=True, help="Whether to run training.") 35 | flags.DEFINE_bool(name="do_eval", default=True, help="Whether to run eval on the dev set.") 36 | flags.DEFINE_bool( 37 | "do_predict", False, 38 | "Whether to run the model in inference mode on the test set.") 39 | flags.DEFINE_bool(name='pb', default=True, help='') 40 | FLAGS = flags.FLAGS 41 | 42 | 43 | def model_fn_builder(): 44 | def model_fn(features, labels, mode, params): 45 | """ 46 | define how to train, evaluate and predict from the transfomer model. 47 | Args: 48 | 49 | mode: 50 | params: 51 | 52 | Returns: 53 | 54 | """ 55 | inputs = features['inputs'] 56 | seq_steps = features['seq_len'] 57 | 58 | 59 | is_training = (mode == tf.estimator.ModeKeys.TRAIN) 60 | 61 | try: 62 | batch_size, length = get_shape_list(inputs, expected_rank=2) 63 | except ValueError: 64 | batch_size = 1 65 | length = get_shape_list(inputs, expected_rank=1)[0] 66 | inputs = tf.reshape(inputs, [batch_size, length]) 67 | 68 | with tf.variable_scope('model'): 69 | # Build model 70 | model = DKT(params, is_training) 71 | logits = model(inputs) # [batch, length, vocab_size] 72 | 73 | # when in prediction mode, the label/target is Bone, the model output is the prediction 74 | if mode == tf.estimator.ModeKeys.PREDICT: 75 | export_outputs = {'predict_output': tf.estimator.export.PredictOutput({"predict": tf.sigmoid(logits)})} 76 | output_spec = tf.estimator.EstimatorSpec(mode=mode, 77 | predictions={'predict': tf.sigmoid(logits)}, 78 | export_outputs=export_outputs 79 | ) 80 | else: 81 | # Calculate model loss 82 | target_ids = features['target_id'] 83 | target_correct = features['target_correct'] 84 | 85 | loss = dkt_loss(logits, target_correct, target_ids, seq_steps) 86 | record_dict = {} 87 | record_dict['minibatch_loss'] = loss 88 | # Save loss as named tensor will be logged with the logging hook 89 | tf.identity(loss, 'cross_entropy') 90 | 91 | if mode == tf.estimator.ModeKeys.EVAL: 92 | metric_dict = get_eval_metrics(logits, target_correct, target_ids, seq_steps) 93 | record_dict['accuracy'] = metric_dict['accuracy'] 94 | record_scalars(record_dict) 95 | output_spec = tf.estimator.EstimatorSpec(mode=tf.estimator.ModeKeys.EVAL, 96 | loss=loss, 97 | predictions={'predict': tf.sigmoid(logits)}, 98 | eval_metric_ops=metric_dict ) 99 | else: # train 100 | # check whether restore from checkpoint 101 | tvars = tf.trainable_variables() 102 | initialized_variable_names = {} 103 | 104 | tf.logging.info("**** Trainable Variables ****") 105 | for var in tvars: 106 | init_string = "" 107 | if var.name in initialized_variable_names: 108 | init_string = ", *INIT_FROM_CKPT*" 109 | tf.logging.info(" name = %s, shape = %s%s", var.name, var.shape, 110 | init_string) 111 | 112 | train_op, metric_dict = get_train_op_and_metrics(loss, params) 113 | acc_metric = get_eval_metrics(logits, target_correct, target_ids, seq_steps) 114 | record_dict['accuracy'] = acc_metric['accuracy'] 115 | record_dict['learning_rate'] = metric_dict['learning_rate'] 116 | record_scalars(record_dict) 117 | output_spec = tf.estimator.EstimatorSpec(mode=tf.estimator.ModeKeys.TRAIN, 118 | loss=loss, 119 | train_op=train_op 120 | ) 121 | return output_spec 122 | return model_fn 123 | 124 | 125 | def input_fn_builder(datadir, epoch): 126 | def input_fn(): 127 | padded_shapes = {'inputs': [None], 128 | 'target_correct': [None], 129 | 'target_id': [None], 130 | 'ids': [None], 131 | 'correct': [None], 132 | 133 | 'seq_len': [], 134 | } 135 | 136 | dataset_train = get_dataset(datadir) 137 | dataset_train = dataset_train.repeat(epoch).shuffle(1000).padded_batch(FLAGS.batch_size, 138 | padded_shapes=padded_shapes) 139 | 140 | return dataset_train 141 | 142 | return input_fn 143 | 144 | 145 | def main(): 146 | # if not FLAGS.do_train and not FLAGS.do_eval and not FLAGS.do_predict: 147 | # raise ValueError("At least one of `do_train`, `do_eval` or `do_predict' must be True.") 148 | params = BASE_PARAMS # param dict from model_params.py 149 | tf.gfile.MakeDirs(FLAGS.saved_model) 150 | 151 | run_config = tf.estimator.RunConfig( 152 | model_dir=FLAGS.saved_model, 153 | tf_random_seed=tf.set_random_seed([42]), 154 | # save_summary_step=100, 155 | save_checkpoints_steps=1000, 156 | # save_checkpoints_secs=600, 157 | session_config=None, 158 | keep_checkpoint_max=5, 159 | keep_checkpoint_every_n_hours=10000, 160 | log_step_count_steps=100, 161 | train_distribute=None 162 | ) 163 | model_fn = model_fn_builder() 164 | estimator = tf.estimator.Estimator( 165 | model_fn=model_fn, 166 | model_dir=FLAGS.saved_model, 167 | config=run_config, 168 | params=params, 169 | warm_start_from=None 170 | ) 171 | # train_input_fn = input_fn_builder(FLAGS.train_data, FLAGS.epoch) 172 | # train_spec = tf.estimator.TrainSpec(input_fn=train_input_fn, 173 | # max_steps=None) 174 | # eval_input_fn = input_fn_builder(FLAGS.valid_data, 1) 175 | # eval_spec = tf.estimator.EvalSpec(input_fn=eval_input_fn, 176 | # steps=None, 177 | # throttle_secs=60) 178 | # tf.estimator.train_and_evaluate(estimator, 179 | # train_spec=train_spec, 180 | # eval_spec=eval_spec) 181 | for epoch in range(FLAGS.epoch): 182 | if FLAGS.do_train: 183 | train_input_fn = input_fn_builder(FLAGS.train_data, 1) 184 | 185 | estimator.train(input_fn=train_input_fn) 186 | if FLAGS.do_eval: 187 | eval_input_fn = input_fn_builder(FLAGS.valid_data, 1) 188 | result = estimator.evaluate(input_fn=eval_input_fn) 189 | 190 | output_eval_file = os.path.join(FLAGS.output_dir, "eval_results.txt") 191 | with tf.gfile.GFile(output_eval_file, "w+") as writer: 192 | tf.logging.info("***** Eval results *****") 193 | for key in sorted(result.keys()): 194 | tf.logging.info(" %s = %s", key, str(result[key])) 195 | writer.write("%s = %s\n" % (key, str(result[key]))) 196 | 197 | 198 | if __name__ == '__main__': 199 | tf.logging.set_verbosity(tf.logging.INFO) 200 | # flags.mark_flag_as_required("train_data") 201 | # flags.mark_flag_as_required("valid_data") 202 | # flags.mark_flag_as_required("saved_model") 203 | 204 | main() 205 | -------------------------------------------------------------------------------- /code/TCN-KT/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:utils.py 5 | @time:2019/8/125:04 PM 6 | """ 7 | from __future__ import absolute_import 8 | from __future__ import division 9 | from __future__ import print_function 10 | 11 | import tensorflow as tf 12 | import collections 13 | import six 14 | import re 15 | 16 | def assert_rank(tensor, expected_rank, name=None): 17 | """Raises an exception if the tensor rank is not of the expected rank. 18 | 19 | Args: 20 | tensor: A tf.Tensor to check the rank of. 21 | expected_rank: Python integer or list of integers, expected rank. 22 | name: Optional name of the tensor for the error message. 23 | 24 | Raises: 25 | ValueError: If the expected shape doesn't match the actual shape. 26 | """ 27 | if name is None: 28 | name = tensor.name 29 | 30 | expected_rank_dict = {} 31 | if isinstance(expected_rank, six.integer_types): 32 | expected_rank_dict[expected_rank] = True 33 | else: 34 | for x in expected_rank: 35 | expected_rank_dict[x] = True 36 | 37 | actual_rank = tensor.shape.ndims 38 | if actual_rank not in expected_rank_dict: 39 | scope_name = tf.get_variable_scope().name 40 | raise ValueError( 41 | "For the tensor `%s` in scope `%s`, the actual rank " 42 | "`%d` (shape = %s) is not equal to the expected rank `%s`" % 43 | (name, scope_name, actual_rank, str(tensor.shape), str(expected_rank))) 44 | 45 | 46 | def get_shape_list(tensor, expected_rank=None, name=None): 47 | """Returns a list of the shape of tensor, preferring static dimensions. 48 | 49 | Args: 50 | tensor: A tf.Tensor object to find the shape of. 51 | expected_rank: (optional) int. The expected rank of `tensor`. If this is 52 | specified and the `tensor` has a different rank, and exception will be 53 | thrown. 54 | name: Optional name of the tensor for the error message. 55 | 56 | Returns: 57 | A list of dimensions of the shape of tensor. All static dimensions will 58 | be returned as python integers, and dynamic dimensions will be returned 59 | as tf.Tensor scalars. 60 | """ 61 | if name is None: 62 | name = tensor.name 63 | 64 | if expected_rank is not None: 65 | assert_rank(tensor, expected_rank, name) 66 | 67 | shape = tensor.shape.as_list() 68 | 69 | non_static_indexes = [] 70 | for (index, dim) in enumerate(shape): 71 | if dim is None: 72 | non_static_indexes.append(index) 73 | 74 | if not non_static_indexes: 75 | return shape 76 | 77 | dyn_shape = tf.shape(tensor) 78 | for index in non_static_indexes: 79 | shape[index] = dyn_shape[index] 80 | return shape 81 | 82 | 83 | def get_learning_rate(learning_rate, hidden_size, learning_rate_warmup_steps): 84 | """calculate learning rate with linear warmup and rsqrt decay""" 85 | with tf.name_scope('learning_rate'): 86 | warmup_steps = tf.to_float(learning_rate_warmup_steps) 87 | step = tf.to_float(tf.train.get_or_create_global_step()) 88 | 89 | learning_rate *= (hidden_size ** -0.5) 90 | # Apply linear warmup 91 | learning_rate *= tf.minimum(1.0, step/warmup_steps) 92 | # Apply rsqrt decay 93 | learning_rate *= tf.rsqrt(tf.maximum(step, warmup_steps)) 94 | 95 | tf.identity(learning_rate, "learning_rate") 96 | return learning_rate 97 | 98 | 99 | def get_train_op_and_metrics(loss, params): 100 | """Generate training op and metrics to save in tensorboard""" 101 | with tf.variable_scope('get_train_op'): 102 | learning_rate = get_learning_rate( 103 | learning_rate=params['learning_rate'], 104 | hidden_size=params['hidden_size'], 105 | learning_rate_warmup_steps=params['learning_rate_warmup_steps'] 106 | ) 107 | # create optimizer , Use lazyAdamOptimizer from TF contrib,which is faster than the TF core Adam operation 108 | optimizer = tf.contrib.opt.LazyAdamOptimizer( 109 | learning_rate=learning_rate, 110 | beta1=params['optimizer_adam_beta1'], 111 | beta2=params['optimizer_adam_beta2'], 112 | epsilon=params['optimizer_adam_epsilon'] 113 | ) 114 | # calculate and apply graph gradient using LazyAdamOptimizer 115 | global_step = tf.train.get_global_step() 116 | tvars = tf.trainable_variables() 117 | gradients = optimizer.compute_gradients(loss, tvars, colocate_gradients_with_ops=True) 118 | minimize_op = optimizer.apply_gradients(gradients, 119 | global_step=global_step, 120 | name='train') 121 | update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) 122 | train_op = tf.group(minimize_op, update_ops) 123 | 124 | train_metrics = {'learning_rate': learning_rate, 125 | 'global_step': global_step} 126 | return train_op, train_metrics 127 | 128 | 129 | def record_scalars(metric_dict): 130 | for key, value in metric_dict.items(): 131 | print('records_scalars', key) 132 | if key == 'accuracy': 133 | tf.summary.scalar(name=key, tensor=value[1]) 134 | else: 135 | tf.summary.scalar(name=key, tensor=value) 136 | 137 | 138 | def get_assignment_map_from_checkpoint(tvars, init_checkpoint): 139 | """Compute the union of the current variables and checkpoint variables.""" 140 | assignment_map = {} 141 | initialized_variable_names = {} 142 | 143 | name_to_variable = collections.OrderedDict() 144 | for var in tvars: 145 | name = var.name 146 | m = re.match("^(.*):\\d+$", name) 147 | if m is not None: 148 | name = m.group(1) 149 | name_to_variable[name] = var 150 | 151 | init_vars = tf.train.list_variables(init_checkpoint) 152 | 153 | assignment_map = collections.OrderedDict() 154 | for x in init_vars: 155 | (name, var) = (x[0], x[1]) 156 | if name not in name_to_variable: 157 | continue 158 | assignment_map[name] = name 159 | initialized_variable_names[name] = 1 160 | initialized_variable_names[name + ":0"] = 1 161 | 162 | return (assignment_map, initialized_variable_names) 163 | 164 | 165 | def parse_exmp(serial_exmp): 166 | feats = tf.parse_single_example(serial_exmp, features={'inputs': tf.VarLenFeature(tf.float32), 167 | 'target_correct': tf.VarLenFeature(tf.int64), 168 | 'target_id': tf.VarLenFeature(tf.float32), 169 | 170 | 'correct': tf.VarLenFeature(tf.int64), 171 | 'id': tf.VarLenFeature(tf.float32), 172 | 173 | 'seq_len': tf.FixedLenFeature([], tf.int64)}) 174 | inputs = tf.sparse_tensor_to_dense(feats['inputs']) # 使用VarLenFeature读入的是一个sparse_tensor,用该函数进行转换 175 | target_correct = tf.sparse_tensor_to_dense(feats['target_correct']) 176 | target_id = tf.sparse_tensor_to_dense(feats['target_id']) 177 | 178 | correct = tf.sparse_tensor_to_dense(feats['correct']) 179 | id = tf.sparse_tensor_to_dense(feats['id']) 180 | 181 | inputs = tf.cast(inputs, tf.int32) 182 | target_correct = tf.cast(target_correct, tf.float32) 183 | target_id = tf.cast(target_id, tf.int32) 184 | 185 | correct = tf.cast(correct, tf.float32) 186 | id = tf.cast(id, tf.int32) 187 | 188 | seq_len = tf.cast(feats['seq_len'], tf.int32) 189 | 190 | return {'inputs': inputs, 191 | 'target_correct': target_correct, 192 | 'target_id': target_id, 193 | 'ids': id, 194 | 'correct': correct, 195 | 'seq_len': seq_len} 196 | 197 | 198 | def get_dataset(fname): 199 | dataset = tf.data.TFRecordDataset(fname) 200 | return dataset.map(parse_exmp) # use padded_batch method if padding needed 201 | -------------------------------------------------------------------------------- /code/Transformer-KT/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:__init__.py.py 5 | @time:2019/8/911:17 AM 6 | """ -------------------------------------------------------------------------------- /code/Transformer-KT/metrics.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:metrics.py 5 | @time:2019/8/122:57 PM 6 | """ 7 | import tensorflow as tf 8 | from model_params import * 9 | from model import DKT 10 | 11 | 12 | def dkt_loss(logits, target_correct, target_ids, seq_steps): 13 | """ 14 | calculate cross-entropy loss for dkt 15 | Args: 16 | logits: logits(no sigmoid func) with shape [batch_size, length, vocab_size] 17 | target_correct: targets with shape [batch_size, length] 18 | target_ids: 19 | seq_steps: 20 | 21 | Returns: 22 | cross-entropy loss 23 | 24 | """ 25 | with tf.name_scope('loss'): 26 | batch_size, length, vocab_size = tf.shape(logits)[0], tf.shape(logits)[1], tf.shape(logits)[2] 27 | flat_logits = tf.reshape(logits, [-1]) 28 | flat_target_correct = tf.reshape(target_correct, [-1]) 29 | 30 | flat_base_target_index = tf.range(batch_size * length) * vocab_size 31 | flat_bias_target_id = tf.reshape(target_ids, [-1]) 32 | flat_target_id = flat_base_target_index + flat_bias_target_id 33 | 34 | flat_target_logits = tf.gather(flat_logits, flat_target_id) 35 | mask = tf.reshape(tf.sequence_mask(seq_steps, maxlen=length), [-1]) 36 | # drop predict which user not react to 37 | flat_target_correct_mask = tf.boolean_mask(flat_target_correct, mask) 38 | flat_target_logits_mask = tf.boolean_mask(flat_target_logits, mask) 39 | 40 | loss = tf.reduce_mean(tf.nn.weighted_cross_entropy_with_logits( 41 | targets=flat_target_correct_mask, 42 | logits=flat_target_logits_mask, 43 | pos_weight=1 44 | )) 45 | return loss 46 | 47 | 48 | def padded_accuracy(logits, target, target_ids, seq_len): 49 | """ 50 | Percentage of times that predictions matches albels on non-0s 51 | Args: 52 | logits: Tensor with shape [batch_size, length, vocab_size] 53 | target: Tesnor wtth shape [batch_size, length] 54 | 55 | Returns: 56 | 57 | """ 58 | with tf.variable_scope('padded_accuracy', values=[logits, target, target_ids, seq_len]): 59 | batch_size, length, vocab_size = tf.shape(logits)[0], tf.shape(logits)[1], tf.shape(logits)[2] 60 | flat_logits = tf.reshape(logits, [-1]) 61 | flat_target_correct = tf.reshape(target, [-1]) 62 | 63 | flat_base_target_index = tf.range(batch_size * length) * vocab_size 64 | flat_bias_target_id = tf.reshape(target_ids, [-1]) 65 | flat_target_id = flat_base_target_index + flat_bias_target_id 66 | 67 | flat_target_logits = tf.gather(flat_logits, flat_target_id) 68 | pred = tf.sigmoid(tf.reshape(flat_target_logits, [batch_size, length])) 69 | # self.binary_pred = tf.cast(tf.greater_equal(self.pred, 0.5), tf.int32) 70 | binary_pred = tf.cast(tf.greater(pred, 0.5), tf.float32) 71 | 72 | predict = tf.reshape(binary_pred, [-1]) 73 | mask = tf.reshape(tf.sequence_mask(seq_len, maxlen=tf.shape(logits)[1]), [-1]) 74 | flat_predict_mask = tf.boolean_mask(predict, mask) 75 | flat_target_mask = tf.boolean_mask(flat_target_correct, mask) 76 | return tf.equal(flat_target_mask, flat_predict_mask) 77 | 78 | 79 | def _convert_to_eval_metric(metric_fn): 80 | """ 81 | warper a metric_fn that returns scores and weights as an eval metric fn 82 | The input metric_fn returns values for the current batch. 83 | The wrapper aggregates the return values collected over all of the batches evaluated 84 | Args: 85 | metric_fn: function that return socres for current batch's logits and targets 86 | 87 | Returns: 88 | function that aggregates the score and weights from metric_fn 89 | 90 | """ 91 | def problem_metric_fn(*args): 92 | """ 93 | return an aggregation of the metric_fn's returned values 94 | Args: 95 | *args: 96 | 97 | Returns: 98 | 99 | """ 100 | score = metric_fn(*args) 101 | return tf.metrics.mean(score) 102 | return problem_metric_fn 103 | 104 | 105 | def get_eval_metrics(logits, labels, target_ids, seq_len): 106 | """ 107 | return dictionary of model evaluation metrics 108 | Args: 109 | logits: 110 | labels: 111 | params: 112 | 113 | Returns: 114 | 115 | """ 116 | metrics = { 117 | 'accuracy': _convert_to_eval_metric(padded_accuracy)(logits, labels, target_ids, seq_len), 118 | } 119 | return metrics -------------------------------------------------------------------------------- /code/Transformer-KT/model.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:model.py 5 | @time:2019/8/95:18 PM 6 | """ 7 | from __future__ import absolute_import 8 | from __future__ import division 9 | from __future__ import print_function 10 | 11 | import tensorflow as tf 12 | from modules import * 13 | import six 14 | 15 | 16 | def assert_rank(tensor, expected_rank, name=None): 17 | """Raises an exception if the tensor rank is not of the expected rank. 18 | 19 | Args: 20 | tensor: A tf.Tensor to check the rank of. 21 | expected_rank: Python integer or list of integers, expected rank. 22 | name: Optional name of the tensor for the error message. 23 | 24 | Raises: 25 | ValueError: If the expected shape doesn't match the actual shape. 26 | """ 27 | if name is None: 28 | name = tensor.name 29 | 30 | expected_rank_dict = {} 31 | if isinstance(expected_rank, six.integer_types): 32 | expected_rank_dict[expected_rank] = True 33 | else: 34 | for x in expected_rank: 35 | expected_rank_dict[x] = True 36 | 37 | actual_rank = tensor.shape.ndims 38 | if actual_rank not in expected_rank_dict: 39 | scope_name = tf.get_variable_scope().name 40 | raise ValueError( 41 | "For the tensor `%s` in scope `%s`, the actual rank " 42 | "`%d` (shape = %s) is not equal to the expected rank `%s`" % 43 | (name, scope_name, actual_rank, str(tensor.shape), str(expected_rank))) 44 | 45 | 46 | def get_shape_list(tensor, expected_rank=None, name=None): 47 | """Returns a list of the shape of tensor, preferring static dimensions. 48 | 49 | Args: 50 | tensor: A tf.Tensor object to find the shape of. 51 | expected_rank: (optional) int. The expected rank of `tensor`. If this is 52 | specified and the `tensor` has a different rank, and exception will be 53 | thrown. 54 | name: Optional name of the tensor for the error message. 55 | 56 | Returns: 57 | A list of dimensions of the shape of tensor. All static dimensions will 58 | be returned as python integers, and dynamic dimensions will be returned 59 | as tf.Tensor scalars. 60 | """ 61 | if name is None: 62 | name = tensor.name 63 | 64 | if expected_rank is not None: 65 | assert_rank(tensor, expected_rank, name) 66 | 67 | shape = tensor.shape.as_list() 68 | 69 | non_static_indexes = [] 70 | for (index, dim) in enumerate(shape): 71 | if dim is None: 72 | non_static_indexes.append(index) 73 | 74 | if not non_static_indexes: 75 | return shape 76 | 77 | dyn_shape = tf.shape(tensor) 78 | for index in non_static_indexes: 79 | shape[index] = dyn_shape[index] 80 | return shape 81 | 82 | 83 | class DKT(object): 84 | def __init__(self, params, train): 85 | """ 86 | Initialize layers to build DKT model 87 | Args: 88 | params: hyperparameter object defining layer size, dropout value etc 89 | train: boolean indicating whether the model is in training mode 90 | """ 91 | self.train = train 92 | self.params = params 93 | 94 | 95 | 96 | self.inputs_embedding = EmbeddingShareWeights(vocab_size= 2*params['vocab_size'], 97 | hidden_size=params['hidden_size']) 98 | self.target_idx_embedding = EmbeddingShareWeights(vocab_size=params['vocab_size'], 99 | hidden_size=params['hidden_size']) 100 | self.encoder = EncoderStack(self.params, self.train) 101 | 102 | def __call__(self, inputs, target_ids): 103 | """ 104 | Calculate logits or inferred target sequence 105 | Args: 106 | self: 107 | inputs: int tensor with shape [batch, input_length] question & reaction encoding 108 | target_ids: int tensor with shape [batch, input_length] question encoding 109 | 110 | input_padding: 111 | 112 | Returns: 113 | 114 | """ 115 | input_shape = get_shape_list(inputs, expected_rank=2) 116 | batch_size = input_shape[0] 117 | length = input_shape[1] 118 | 119 | target_ids = tf.reshape(target_ids, [batch_size, length]) 120 | initializer = tf.variance_scaling_initializer( 121 | self.params['initializer_gain'], 122 | mode="fan_avg", 123 | distribution="uniform") 124 | with tf.variable_scope('DKT', initializer=initializer): 125 | inputs_embeddings = self.inputs_embedding(inputs) # shape = [batch, length, hidden_size-feature_size] 126 | 127 | target_ids_embeddings = self.target_idx_embedding(target_ids) # shape=[batch, length, hidden_size] 128 | 129 | with tf.name_scope("add_pos_encoding"): 130 | length = tf.shape(inputs_embeddings)[1] 131 | pos_encoding = position_encoding(length, self.params['hidden_size']) 132 | encoder_key = inputs_embeddings + pos_encoding 133 | encoder_query = target_ids_embeddings + pos_encoding 134 | 135 | if self.train: 136 | encoder_key = tf.nn.dropout(encoder_key, keep_prob=1-self.params['layer_postprocess_dropout']) 137 | encoder_query = tf.nn.dropout(encoder_query, keep_prob=1-self.params['layer_postprocess_dropout']) 138 | attention_bias = get_padding_bias(encoder_key) 139 | inputs_padding = get_padding(encoder_key) 140 | # shape=[batch, length, hidden_size] 141 | transformer_output = self.encoder(encoder_key, encoder_query=encoder_query, encoder_key=encoder_key, 142 | attention_bias=attention_bias, inputs_padding=inputs_padding) 143 | 144 | with tf.name_scope("Outout_layer"): 145 | # shape=[batch, length, vocab_size] 146 | logits = tf.layers.dense(transformer_output, units=self.params['vocab_size'], activation=None) # linear layer 147 | 148 | return logits 149 | 150 | 151 | class PrepostProcessingWrapper(object): 152 | """Wrapper class that applies layer pre-processing and post-processing""" 153 | 154 | def __init__(self, layer, params, train): 155 | self.layer = layer 156 | self.postprocess_dropout = params['layer_postprocess_dropout'] 157 | self.train = train 158 | 159 | self.layer_norm = LayerNormalization(params['hidden_size']) 160 | 161 | def __call__(self, x, *args, **kwargs): 162 | y = self.layer_norm(x) 163 | 164 | y = self.layer(y, *args, **kwargs) 165 | if self.train: 166 | y = tf.nn.dropout(y, keep_prob=1 - self.postprocess_dropout) 167 | return x + y 168 | 169 | 170 | class EncoderStack(tf.layers.Layer): 171 | """Transfomer encoder stack""" 172 | def __init__(self, params, train): 173 | super(EncoderStack, self).__init__() 174 | self.layers = [] 175 | 176 | for i in range(params['num_hidden_layers']): 177 | # create sublayers for each layer 178 | self_attention_layer = Attention(hidden_size=params['hidden_size'], 179 | num_head=params['num_heads'], 180 | attention_dropout=params['attention_dropout'], 181 | train=train) 182 | feed_forward_netword = FeedForwardNetwork(hidden_size=params['hidden_size'], 183 | filter_size=params['filter_size'], 184 | relu_dropout=params['relu_dropout'], 185 | train=train, 186 | allow_pad=params['allow_ffn_pad']) 187 | self.layers.append([ 188 | PrepostProcessingWrapper(self_attention_layer, params, train), 189 | PrepostProcessingWrapper(feed_forward_netword, params, train) 190 | ]) 191 | 192 | self.output_normalization = LayerNormalization(params['hidden_size']) 193 | 194 | def call(self, inputs, encoder_query, encoder_key, attention_bias, inputs_padding): 195 | """ 196 | Return the output of the encoder of layer stacks 197 | Args: 198 | encoder_query: tensor with shape [batch_size, input_length, hidden_size] query 199 | encoder_key: tensor with shape [batch_size, input_length, hidden_size] key & value 200 | attention_bias: bias for encoder self-attention layer [batch, 1, 1, input_length] 201 | input_padding: Padding 202 | 203 | Returns: 204 | output of encoder layer stack. 205 | float 32 tensor with shape [batch_size, input_length, hidden_size] 206 | """ 207 | for n, layer in enumerate(self.layers): 208 | self_attention_layer = layer[0] 209 | feed_forward_network = layer[1] 210 | 211 | with tf.variable_scope("layer_%d" %n): 212 | with tf.variable_scope("self_attention"): 213 | if n == 0: 214 | encoder_inputs = self_attention_layer(x=encoder_query, y=encoder_key, bias=attention_bias) 215 | else: 216 | encoder_inputs = self_attention_layer(x=encoder_inputs, y=encoder_inputs, bias=attention_bias) 217 | with tf.variable_scope("ffn"): 218 | encoder_inputs = feed_forward_network(encoder_inputs, padding=None) 219 | return self.output_normalization(encoder_inputs) 220 | -------------------------------------------------------------------------------- /code/Transformer-KT/model_params.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:model_params.py 5 | @time:2019/8/1212:42 PM 6 | """ 7 | from collections import defaultdict 8 | 9 | 10 | BASE_PARAMS = defaultdict( 11 | # Input params 12 | default_batch_size=256, # Maximum number of tokens per batch of examples. 13 | max_length=512, # Maximum number of tokens per example. 14 | 15 | 16 | # Model Params 17 | initializer_gain=1.0, # Used in trainable variable initialization 18 | vocab_size=124, # unique tokes defined in the vocabulary (num_skills) 19 | hidden_size=256, # hidden size 20 | num_hidden_layers=2, # number of layers in the encoder 21 | num_heads=8, # number of heads to use in the multi-head attention 22 | 23 | filter_size=512, # Inner layer dimension in the feedward netword 24 | allow_ffn_pad=True, 25 | 26 | # Dropout values(Only used in training) 27 | layer_postprocess_dropout=0.1, 28 | attention_dropout=0.1, 29 | relu_dropout=0.1, 30 | 31 | 32 | # Training params 33 | learning_rate=0.1, 34 | learning_rate_decay_rate=1.0, 35 | learning_rate_warmup_steps=16000, 36 | 37 | # Optimizer params 38 | optimizer_adam_beta1=0.9, 39 | optimizer_adam_beta2=0.997, 40 | optimizer_adam_epsilon=1e-09, 41 | 42 | ) -------------------------------------------------------------------------------- /code/Transformer-KT/modules.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:modules.py 5 | @time:2019/8/911:18 AM 6 | """ 7 | from __future__ import absolute_import 8 | from __future__ import division 9 | from __future__ import print_function 10 | 11 | import tensorflow as tf 12 | import math 13 | import six 14 | 15 | 16 | class EmbeddingShareWeights(tf.layers.Layer): 17 | """ 18 | Calculates input embeddings 19 | """ 20 | def __init__(self, vocab_size, hidden_size): 21 | """ 22 | Specify characteristic parameters of embedding layer 23 | Args: 24 | vocab_size: Number of tokens in the embedding 25 | hidden_size: Dimensionality of the embedding. 26 | """ 27 | super(EmbeddingShareWeights, self).__init__() 28 | self.vocab_size = vocab_size 29 | self.hidden_size = hidden_size 30 | 31 | def build(self, _): 32 | with tf.variable_scope("embedding_n_softmax", reuse=tf.AUTO_REUSE): 33 | """ 34 | Create the initialize weights 35 | """ 36 | self.shared_weight = tf.get_variable( 37 | name="weights", shape=[self.vocab_size, self.hidden_size], 38 | initializer=tf.random_normal_initializer(mean=0.0, stddev=self.hidden_size ** -0.5)) 39 | self.built = True 40 | 41 | def call(self, x): 42 | """ 43 | Get token embeddings of x 44 | Args: 45 | x: An int64 tensor with shape [batch, length] 46 | Returns: 47 | embeddings: float32. Tensor with shape [batch, length, embedding_szie] 48 | padding: float32. Tensor with shape [batch, length] indicating the locations of the padding tokens in x. 49 | """ 50 | with tf.name_scope("embeddings"): 51 | mask = tf.to_float(tf.not_equal(x, 0)) 52 | embeddings = tf.gather(self.shared_weight, tf.cast(x, tf.int64)) 53 | embeddings *= tf.expand_dims(mask, -1) 54 | 55 | embeddings *= self.hidden_size ** 0.5 # scale embedding by the sqrt of the hidden size 56 | return embeddings 57 | 58 | 59 | def position_encoding(length, hidden_size, min_timescale=1.0, max_timescale=1.0e4): 60 | """ 61 | Calculate the position encoding as a mix of sine and cosine function with geometrically 62 | increasing wavelengths. 63 | Defined and formulized in Attention is all your need. 64 | Args: 65 | length: sequence lenght 66 | hidden_size: size of the embedding 67 | min_timescale: Minimum scale that will be applied at each position 68 | max_timescale: Maximum scale that will be applied at each position 69 | 70 | Returns: 71 | Tensor with shape [length, hidden_size] 72 | 73 | """ 74 | position = tf.cast(tf.range(length), tf.float32) 75 | num_timescales = hidden_size // 2 76 | log_timescale_increment = (math.log(float(max_timescale) / float(min_timescale)) / 77 | (tf.cast(num_timescales, tf.float32) -1)) 78 | inv_timescales = min_timescale * tf.exp( 79 | tf.cast(tf.range(num_timescales), tf.float32) * -log_timescale_increment) # shape= [hidden_size/2] 80 | scaled_time = tf.expand_dims(position, 1) * tf.expand_dims(inv_timescales, 0) # shape= [length, hidden_size/2] 81 | signal = tf.concat([tf.sin(scaled_time), tf.cos(scaled_time)], axis=1) # shape= [length, hidden_size] 82 | 83 | return signal 84 | 85 | 86 | class Attention(tf.layers.Layer): 87 | """Multi-headed attention layer""" 88 | def __init__(self, hidden_size, num_head, attention_dropout, train): 89 | if hidden_size % num_head != 0: 90 | raise ValueError("Hidden_size must be evenly divisible by the number of heads") 91 | 92 | super(Attention, self).__init__() 93 | self.hidden_size = hidden_size 94 | self.num_head = num_head 95 | self.attention_dropout = attention_dropout 96 | self.train = train 97 | 98 | # Layer for linear projecting query, key, values: 99 | self.q_dense_layer = tf.layers.Dense(units=hidden_size, use_bias=False, name='q') 100 | self.k_dense_layer = tf.layers.Dense(units=hidden_size, use_bias=False, name='k') 101 | self.v_dense_layer = tf.layers.Dense(units=hidden_size, use_bias=False, name='v') 102 | 103 | self.output_dense_layer = tf.layers.Dense(units=hidden_size, use_bias=False, name='output_transformer') 104 | 105 | def split_heads(self, x): 106 | """ 107 | Split x into different heads, and transpose the resulting value 108 | Args: 109 | x: A tensor with shape [batch, length, hidden_size] 110 | 111 | Returns: 112 | A tensor with shape[batch, num_head, length, hidden_size] 113 | """ 114 | with tf.name_scope("split_heads"): 115 | batch_size, length = tf.shape(x)[0], tf.shape(x)[1] 116 | 117 | depth = (self.hidden_size // self.num_head) 118 | 119 | x = tf.reshape(x, [batch_size, length, self.num_head, depth]) 120 | 121 | return tf.transpose(x, [0, 2, 1, 3]) 122 | 123 | def combine_heads(self, x): 124 | """ 125 | combine tensor that has been split 126 | Args: 127 | x: A tensor with shape [batch_size, num_heads, length, hidden_size/num_heads] 128 | 129 | Returns: 130 | A tensor with shape [batch, length, hidden_size] 131 | """ 132 | with tf.name_scope("combine_heads"): 133 | batch_size, length = tf.shape(x)[0], tf.shape(x)[2] 134 | x = tf.transpose(x, [0, 2, 1, 3]) # --> [batch, length, num_heads, hidden_size/num_heads] 135 | return tf.reshape(x, [batch_size, length, self.hidden_size]) 136 | 137 | def call(self, x, y, bias, cache=None): 138 | """ 139 | Apply attention mechanism to x and y 140 | Args: 141 | x: A tensor with shape [batch, length_x, hidden_size] 142 | y: A tensor with shape [batch, length_y, hidden_size] 143 | bias: attention bias that will be add to the result of the dot product. 144 | cache: (Used during prediction) dictionary with tensor containing results of previus attentions. 145 | The dictionary must have the items: 146 | {'k': tensor with shape [batch, i, key_channels], 147 | 'v': tensor with shape [batch, i, value_channels]} 148 | where i is the current decoded length 149 | 150 | Returns: 151 | Attention layer output with shape [batch, length_x, hidden_size] 152 | 153 | """ 154 | length = tf.shape(x)[1] 155 | q = self.q_dense_layer(x) # [batch, length, hidden_size] 156 | k = self.k_dense_layer(y) 157 | v = self.v_dense_layer(y) 158 | 159 | if cache is not None: 160 | # combine cached keys and values with new keys and values 161 | k = tf.concat([cache['k'], k], axis=1) 162 | v = tf.concat([cache['v'], v], axis=1) 163 | 164 | # update cache: 165 | cache['k'] = k 166 | cache['v'] = v 167 | 168 | # split q,k,v into heads: 169 | q = self.split_heads(q) 170 | k = self.split_heads(k) 171 | v = self.split_heads(v) # [batch_size, length, num_head, hidden_size//num_head] 172 | 173 | # scale q to prevent dot product between q and k from growing too large 174 | depth = self.hidden_size // self.num_head 175 | q *= depth ** -0.5 176 | 177 | # calculate dot product attention 178 | logits = tf.matmul(q, k, transpose_b=True) 179 | 180 | # add mask to prevent future words 181 | mask = create_look_ahead_mask(length) 182 | logits += mask 183 | weight = tf.nn.softmax(logits, name='attention_weigths') 184 | if self.train: 185 | weight = tf.nn.dropout(weight, keep_prob=1-self.attention_dropout) 186 | attention_output = tf.matmul(weight, v) 187 | 188 | # Recombine heads --> [batch, length, hidden_size] 189 | attention_output = self.combine_heads(attention_output) 190 | 191 | # Run the combined output through another linear projection layers 192 | attention_output = self.output_dense_layer(attention_output) 193 | return attention_output 194 | 195 | 196 | class SelfAttention(Attention): 197 | """ 198 | multihead self-attention layer. 199 | """ 200 | def call(self, x, bias, cache=None): 201 | return super(SelfAttention, self).call(x, x, bias, cache) 202 | 203 | 204 | class FeedForwardNetwork(tf.layers.Layer): 205 | """Fully connected feedforward network""" 206 | def __init__(self, hidden_size, filter_size, relu_dropout, train, allow_pad): 207 | super(FeedForwardNetwork, self).__init__() 208 | self.hidden_size = hidden_size 209 | self.filter_size = filter_size 210 | self.relu_dropout = relu_dropout 211 | self.train = train 212 | self.allow_pad = allow_pad 213 | 214 | self.filter_dense_layer = tf.layers.Dense(filter_size, use_bias=True, activation=tf.nn.relu, 215 | name='filter_layer') 216 | self.output_dense_layer = tf.layers.Dense(hidden_size, use_bias=True, name='output_layer') 217 | 218 | def call(self, x, padding=None): 219 | """ 220 | Return outputs of the feedforward network" 221 | Args: 222 | x: Tensor with shape [batch_size, length, hidden_size] 223 | padding: Optional, if set, the padding values are temporatily removed from x. 224 | The padding values are placed back in the output tensor in the same locations. 225 | shape [batch, length] 226 | Returns: 227 | Output of the feed forward network 228 | shape [batch, length, hidden_size] 229 | """ 230 | padding = None if not self.allow_pad else padding 231 | batch_size, length = tf.shape(x)[0], tf.shape(x)[1] 232 | 233 | if padding is not None: 234 | with tf.name_scope('remove_padding'): 235 | pad_mask = tf.reshape(padding, [-1, self.hidden_size]) 236 | 237 | nonpad_ids = tf.to_int32(tf.where(pad_mask < 1e-9)) 238 | 239 | # reshape x to [batch*length, hidden_size] to remove padding 240 | x = tf.reshape(x, shape=[-1, self.hidden_size]) 241 | x = tf.gather_nd(x, indices=nonpad_ids) 242 | 243 | # Reshape x from 2 dimensions to 3 dimensions 244 | x.set_shape([None, self.hidden_size]) 245 | x = tf.expand_dims(x, axis=0) 246 | 247 | output = self.filter_dense_layer(x) 248 | if self.train: 249 | output = tf.nn.dropout(output, keep_prob=1-self.relu_dropout) 250 | output = self.output_dense_layer(output) 251 | 252 | if padding is not None: 253 | with tf.name_scope('re_add_padding'): 254 | output = tf.squeeze(output, axis=0) 255 | output = tf.scatter_nd(indices=nonpad_ids, updates=output, 256 | shape=[batch_size*length, self.hidden_size]) 257 | output = tf.reshape(output, [batch_size, length, self.hidden_size]) 258 | return output 259 | 260 | 261 | class LayerNormalization(tf.layers.Layer): 262 | """ 263 | Apply layer normalization 264 | """ 265 | def __init__(self, hidden_size): 266 | super(LayerNormalization, self).__init__() 267 | self.hidden_size = hidden_size 268 | 269 | def build(self, _): 270 | self.scale = tf.get_variable(name='layer_norm_scale', 271 | shape=[self.hidden_size], 272 | initializer=tf.ones_initializer()) 273 | self.bias = tf.get_variable(name='layer_norm_bias', 274 | shape=[self.hidden_size], 275 | initializer=tf.zeros_initializer()) 276 | self.build = True 277 | 278 | def call(self, x, epsilon=1e-6): 279 | mean = tf.reduce_mean(x, axis=[-1], keep_dims=True) 280 | variance = tf.reduce_mean(tf.square(x - mean), axis=[-1], keepdims=True) 281 | norm_x = (x - mean) * tf.sqrt(variance + epsilon) 282 | return norm_x * self.scale + self.bias 283 | 284 | 285 | def get_padding(x, padding_value=0, dtype=tf.float32): 286 | """ 287 | 288 | Args: 289 | x: int tensor with any shape 290 | padding_value: int value which padding value set 291 | dtype: The dtype of the return value 292 | 293 | Returns: 294 | float tensor with same shape as x containing value 0,1 295 | 0 means non-padding, 1 means padding 296 | 297 | """ 298 | with tf.name_scope('padding'): 299 | return tf.cast(tf.equal(x, padding_value), dtype) 300 | 301 | 302 | def get_padding_bias(x): 303 | """ 304 | calculate bias tensot from padding values in tensor 305 | 306 | bias tensor that is added to the pre-softmax multi-head attention logits, 307 | which has shape [batch_size, num_heads, length, length] 308 | The tensor is zero at non-padding locations, and -1e9(negtive infinity) at padding locations 309 | Args: 310 | x:int tensor with shape [batch_size, length] 311 | Returns: 312 | Attention bias tensor of shape [batch_size, 1, 1, length] 313 | 314 | """ 315 | with tf.name_scope('attention_bias'): 316 | padding = get_padding(x) 317 | attention_bias = padding * -1e9 318 | attention_bias = tf.expand_dims(attention_bias, axis=1) 319 | return attention_bias 320 | 321 | 322 | def create_look_ahead_mask(length, dtype=tf.float32): 323 | 324 | """Calculate bias for decoder that maintains model's autoregressive property. 325 | Creates a tensor that masks out locations that correspond to illegal 326 | connections, so prediction at position i cannot draw information from future 327 | positions. 328 | Args: 329 | length: int length of sequences in batch. 330 | dtype: The dtype of the return value. 331 | Returns: 332 | float tensor of shape [1, 1, length, length] 333 | """ 334 | neg_inf = -1e9 335 | with tf.name_scope("decoder_self_attention_bias"): 336 | valid_locs = tf.linalg.band_part(tf.ones([length, length], dtype=dtype), 337 | -1, 0) 338 | valid_locs = tf.reshape(valid_locs, [1, 1, length, length]) 339 | decoder_bias = neg_inf * (1.0 - valid_locs) 340 | return decoder_bias 341 | 342 | 343 | -------------------------------------------------------------------------------- /code/Transformer-KT/run_dkt.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:run_dkt.py 5 | @time:2019/8/1212:39 PM 6 | """ 7 | from __future__ import absolute_import 8 | from __future__ import division 9 | from __future__ import print_function 10 | 11 | import tensorflow as tf 12 | import os 13 | from model_params import BASE_PARAMS 14 | from model import * 15 | from metrics import * 16 | from utils import * 17 | 18 | 19 | 20 | flags = tf.flags 21 | logging = tf.logging 22 | 23 | flags.DEFINE_string(name='train_data', default='../data/train.tfrecord', 24 | help='Path of training data ') 25 | flags.DEFINE_string(name='valid_data', default='../data/eval.tfrecord', 26 | help='Path of valid data') 27 | flags.DEFINE_string(name='predict_data', default='', 28 | help='Path of predict data') 29 | flags.DEFINE_string(name='saved_model', default='./saved_model', help='Path to save model') 30 | flags.DEFINE_string(name='output_dir', default='./output_dir', help='Path to save model') 31 | flags.DEFINE_integer(name='epoch', default=100, help='Num of epoch') 32 | flags.DEFINE_integer(name='batch_size', default=1, help='Num of batch') 33 | flags.DEFINE_bool(name="do_train", default=True, help="Whether to run training.") 34 | flags.DEFINE_bool(name="do_eval", default=True, help="Whether to run eval on the dev set.") 35 | flags.DEFINE_bool( 36 | "do_predict", False, 37 | "Whether to run the model in inference mode on the test set.") 38 | flags.DEFINE_bool(name='pb', default=True, help='') 39 | FLAGS = flags.FLAGS 40 | 41 | 42 | def model_fn_builder(): 43 | def model_fn(features, labels, mode, params): 44 | """ 45 | define how to train, evaluate and predict from the transfomer model. 46 | Args: 47 | 48 | mode: 49 | params: 50 | 51 | Returns: 52 | 53 | """ 54 | inputs = features['inputs'] 55 | target_ids = features['target_id'] 56 | 57 | is_training = (mode == tf.estimator.ModeKeys.TRAIN) 58 | 59 | try: 60 | batch_size, length = get_shape_list(inputs, expected_rank=2) 61 | except ValueError: 62 | batch_size = 1 63 | length = get_shape_list(inputs, expected_rank=1)[0] 64 | inputs = tf.reshape(inputs, [batch_size, length]) 65 | 66 | with tf.variable_scope('model'): 67 | # Build model 68 | model = DKT(params, is_training) 69 | logits = model(inputs, target_ids) # [batch, length, vocab_size] 70 | 71 | # when in prediction mode, the label/target is Bone, the model output is the prediction 72 | if mode == tf.estimator.ModeKeys.PREDICT: 73 | export_outputs = {'predict_output': tf.estimator.export.PredictOutput({"predict": tf.sigmoid(logits)})} 74 | output_spec = tf.estimator.EstimatorSpec(mode=mode, 75 | predictions={'predict': tf.sigmoid(logits)}, 76 | export_outputs=export_outputs 77 | ) 78 | else: 79 | # Calculate model loss 80 | seq_steps = features['seq_len'] 81 | target_correct = features['target_correct'] 82 | 83 | loss = dkt_loss(logits, target_correct, target_ids, seq_steps) 84 | record_dict = {} 85 | record_dict['minibatch_loss'] = loss 86 | # Save loss as named tensor will be logged with the logging hook 87 | tf.identity(loss, 'cross_entropy') 88 | 89 | if mode == tf.estimator.ModeKeys.EVAL: 90 | metric_dict = get_eval_metrics(logits, target_correct, target_ids, seq_steps) 91 | record_dict['accuracy'] = metric_dict['accuracy'] 92 | record_scalars(record_dict) 93 | output_spec = tf.estimator.EstimatorSpec(mode=tf.estimator.ModeKeys.EVAL, 94 | loss=loss, 95 | predictions={'predict': tf.sigmoid(logits)}, 96 | eval_metric_ops=metric_dict ) 97 | else: # train 98 | # check whether restore from checkpoint 99 | tvars = tf.trainable_variables() 100 | initialized_variable_names = {} 101 | 102 | tf.logging.info("**** Trainable Variables ****") 103 | for var in tvars: 104 | init_string = "" 105 | if var.name in initialized_variable_names: 106 | init_string = ", *INIT_FROM_CKPT*" 107 | tf.logging.info(" name = %s, shape = %s%s", var.name, var.shape, 108 | init_string) 109 | 110 | train_op, metric_dict = get_train_op_and_metrics(loss, params) 111 | acc_metric = get_eval_metrics(logits, target_correct, target_ids, seq_steps) 112 | record_dict['accuracy'] = acc_metric['accuracy'] 113 | record_dict['learning_rate'] = metric_dict['learning_rate'] 114 | record_scalars(record_dict) 115 | output_spec = tf.estimator.EstimatorSpec(mode=tf.estimator.ModeKeys.TRAIN, 116 | loss=loss, 117 | train_op=train_op 118 | ) 119 | return output_spec 120 | return model_fn 121 | 122 | 123 | def input_fn_builder(datadir, epoch): 124 | def input_fn(): 125 | padded_shapes = {'inputs': [None], 126 | 'target_correct': [None], 127 | 'target_id': [None], 128 | 'ids': [None], 129 | 'correct': [None], 130 | 131 | 'seq_len': [], 132 | } 133 | 134 | dataset_train = get_dataset(datadir) 135 | dataset_train = dataset_train.repeat(epoch).shuffle(1000).padded_batch(FLAGS.batch_size, 136 | padded_shapes=padded_shapes) 137 | 138 | return dataset_train 139 | 140 | return input_fn 141 | 142 | 143 | def main(): 144 | # if not FLAGS.do_train and not FLAGS.do_eval and not FLAGS.do_predict: 145 | # raise ValueError("At least one of `do_train`, `do_eval` or `do_predict' must be True.") 146 | params = BASE_PARAMS # param dict from model_params.py 147 | tf.gfile.MakeDirs(FLAGS.saved_model) 148 | 149 | run_config = tf.estimator.RunConfig( 150 | model_dir=FLAGS.saved_model, 151 | tf_random_seed=tf.set_random_seed([42]), 152 | # save_summary_step=100, 153 | save_checkpoints_steps=1000, 154 | # save_checkpoints_secs=600, 155 | session_config=None, 156 | keep_checkpoint_max=5, 157 | keep_checkpoint_every_n_hours=10000, 158 | log_step_count_steps=100, 159 | train_distribute=None 160 | ) 161 | model_fn = model_fn_builder() 162 | estimator = tf.estimator.Estimator( 163 | model_fn=model_fn, 164 | model_dir=FLAGS.saved_model, 165 | config=run_config, 166 | params=params, 167 | warm_start_from=None 168 | ) 169 | # train_input_fn = input_fn_builder(FLAGS.train_data, FLAGS.epoch) 170 | # train_spec = tf.estimator.TrainSpec(input_fn=train_input_fn, 171 | # max_steps=None) 172 | # eval_input_fn = input_fn_builder(FLAGS.valid_data, 1) 173 | # eval_spec = tf.estimator.EvalSpec(input_fn=eval_input_fn, 174 | # steps=None, 175 | # throttle_secs=60) 176 | # tf.estimator.train_and_evaluate(estimator, 177 | # train_spec=train_spec, 178 | # eval_spec=eval_spec) 179 | for epoch in range(FLAGS.epoch): 180 | if FLAGS.do_train: 181 | train_input_fn = input_fn_builder(FLAGS.train_data, 1) 182 | 183 | estimator.train(input_fn=train_input_fn) 184 | if FLAGS.do_eval: 185 | eval_input_fn = input_fn_builder(FLAGS.valid_data, 1) 186 | result = estimator.evaluate(input_fn=eval_input_fn) 187 | 188 | output_eval_file = os.path.join(FLAGS.output_dir, "eval_results.txt") 189 | with tf.gfile.GFile(output_eval_file, "w+") as writer: 190 | tf.logging.info("***** Eval results *****") 191 | for key in sorted(result.keys()): 192 | tf.logging.info(" %s = %s", key, str(result[key])) 193 | writer.write("%s = %s\n" % (key, str(result[key]))) 194 | 195 | 196 | if __name__ == '__main__': 197 | tf.logging.set_verbosity(tf.logging.INFO) 198 | # flags.mark_flag_as_required("train_data") 199 | # flags.mark_flag_as_required("valid_data") 200 | # flags.mark_flag_as_required("saved_model") 201 | 202 | main() 203 | -------------------------------------------------------------------------------- /code/Transformer-KT/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:utils.py 5 | @time:2019/8/125:04 PM 6 | """ 7 | from __future__ import absolute_import 8 | from __future__ import division 9 | from __future__ import print_function 10 | 11 | import tensorflow as tf 12 | import collections 13 | import re 14 | 15 | 16 | def get_learning_rate(learning_rate, hidden_size, learning_rate_warmup_steps): 17 | """calculate learning rate with linear warmup and rsqrt decay""" 18 | with tf.name_scope('learning_rate'): 19 | warmup_steps = tf.to_float(learning_rate_warmup_steps) 20 | step = tf.to_float(tf.train.get_or_create_global_step()) 21 | 22 | learning_rate *= (hidden_size ** -0.5) 23 | # Apply linear warmup 24 | learning_rate *= tf.minimum(1.0, step/warmup_steps) 25 | # Apply rsqrt decay 26 | learning_rate *= tf.rsqrt(tf.maximum(step, warmup_steps)) 27 | 28 | tf.identity(learning_rate, "learning_rate") 29 | return learning_rate 30 | 31 | 32 | def get_train_op_and_metrics(loss, params): 33 | """Generate training op and metrics to save in tensorboard""" 34 | with tf.variable_scope('get_train_op'): 35 | learning_rate = get_learning_rate( 36 | learning_rate=params['learning_rate'], 37 | hidden_size=params['hidden_size'], 38 | learning_rate_warmup_steps=params['learning_rate_warmup_steps'] 39 | ) 40 | # create optimizer , Use lazyAdamOptimizer from TF contrib,which is faster than the TF core Adam operation 41 | optimizer = tf.contrib.opt.LazyAdamOptimizer( 42 | learning_rate=learning_rate, 43 | beta1=params['optimizer_adam_beta1'], 44 | beta2=params['optimizer_adam_beta2'], 45 | epsilon=params['optimizer_adam_epsilon'] 46 | ) 47 | # calculate and apply graph gradient using LazyAdamOptimizer 48 | global_step = tf.train.get_global_step() 49 | tvars = tf.trainable_variables() 50 | gradients = optimizer.compute_gradients(loss, tvars, colocate_gradients_with_ops=True) 51 | minimize_op = optimizer.apply_gradients(gradients, 52 | global_step=global_step, 53 | name='train') 54 | update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) 55 | train_op = tf.group(minimize_op, update_ops) 56 | 57 | train_metrics = {'learning_rate': learning_rate, 58 | 'global_step': global_step} 59 | return train_op, train_metrics 60 | 61 | 62 | def record_scalars(metric_dict): 63 | for key, value in metric_dict.items(): 64 | print('records_scalars', key) 65 | if key == 'accuracy': 66 | tf.summary.scalar(name=key, tensor=value[1]) 67 | else: 68 | tf.summary.scalar(name=key, tensor=value) 69 | 70 | 71 | def get_assignment_map_from_checkpoint(tvars, init_checkpoint): 72 | """Compute the union of the current variables and checkpoint variables.""" 73 | assignment_map = {} 74 | initialized_variable_names = {} 75 | 76 | name_to_variable = collections.OrderedDict() 77 | for var in tvars: 78 | name = var.name 79 | m = re.match("^(.*):\\d+$", name) 80 | if m is not None: 81 | name = m.group(1) 82 | name_to_variable[name] = var 83 | 84 | init_vars = tf.train.list_variables(init_checkpoint) 85 | 86 | assignment_map = collections.OrderedDict() 87 | for x in init_vars: 88 | (name, var) = (x[0], x[1]) 89 | if name not in name_to_variable: 90 | continue 91 | assignment_map[name] = name 92 | initialized_variable_names[name] = 1 93 | initialized_variable_names[name + ":0"] = 1 94 | 95 | return (assignment_map, initialized_variable_names) 96 | 97 | 98 | def parse_exmp(serial_exmp): 99 | feats = tf.parse_single_example(serial_exmp, features={'inputs': tf.VarLenFeature(tf.float32), 100 | 'target_correct': tf.VarLenFeature(tf.int64), 101 | 'target_id': tf.VarLenFeature(tf.float32), 102 | 103 | 'correct': tf.VarLenFeature(tf.int64), 104 | 'id': tf.VarLenFeature(tf.float32), 105 | 106 | 'seq_len': tf.FixedLenFeature([], tf.int64)}) 107 | inputs = tf.sparse_tensor_to_dense(feats['inputs']) # 使用VarLenFeature读入的是一个sparse_tensor,用该函数进行转换 108 | target_correct = tf.sparse_tensor_to_dense(feats['target_correct']) 109 | target_id = tf.sparse_tensor_to_dense(feats['target_id']) 110 | 111 | correct = tf.sparse_tensor_to_dense(feats['correct']) 112 | id = tf.sparse_tensor_to_dense(feats['id']) 113 | 114 | inputs = tf.cast(inputs, tf.int32) 115 | target_correct = tf.cast(target_correct, tf.float32) 116 | target_id = tf.cast(target_id, tf.int32) 117 | 118 | correct = tf.cast(correct, tf.float32) 119 | id = tf.cast(id, tf.int32) 120 | 121 | seq_len = tf.cast(feats['seq_len'], tf.int32) 122 | 123 | return {'inputs': inputs, 124 | 'target_correct': target_correct, 125 | 'target_id': target_id, 126 | 'ids': id, 127 | 'correct': correct, 128 | 'seq_len': seq_len} 129 | 130 | 131 | def get_dataset(fname): 132 | dataset = tf.data.TFRecordDataset(fname) 133 | return dataset.map(parse_exmp) # use padded_batch method if padding needed 134 | -------------------------------------------------------------------------------- /code/data/Assistment/2009-2010_skill_builder_data.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sulingling123/Knowledge_Tracing/e47425c068066190c0a435dc1ba1ce2c2a9c56ec/code/data/Assistment/2009-2010_skill_builder_data.csv -------------------------------------------------------------------------------- /code/data/csv2tfrecords.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | """ 3 | @author:Zoe 4 | @file:csv2tfrecords.py 5 | @time:2019/10/85:43 PM 6 | """ 7 | import numpy as np 8 | from collections import defaultdict 9 | import tensorflow as tf 10 | import random 11 | import csv 12 | 13 | 14 | class EDM_Processor(object): 15 | """Processor for the MultiNLI data set (GLUE version).""" 16 | def __init__(self): 17 | self.num_skill = None 18 | self.max_len = None 19 | 20 | def _read_tsv(self, dataset_path): 21 | """Reads a tab separated value file.""" 22 | rows = [] 23 | max_skill_num = 0 24 | max_num_problems = 0 25 | with open(dataset_path, "r") as csvfile: 26 | reader = csv.reader(csvfile, delimiter=',') 27 | for row in reader: 28 | rows.append(row) 29 | index = 0 30 | i = 0 31 | print("the number of rows is " + str(len(rows))) 32 | tuple_rows = [] 33 | # turn list to tuple 34 | while (index < len(rows) - 1): 35 | problems_num = int(rows[index][0]) 36 | tmp_max_skill = max(map(int, rows[index + 1])) 37 | if (tmp_max_skill > max_skill_num): 38 | max_skill_num = tmp_max_skill 39 | if (problems_num <= 2): 40 | index += 3 41 | else: 42 | if problems_num > max_num_problems: 43 | max_num_problems = problems_num 44 | tup = (rows[index], rows[index + 1], rows[index + 2]) 45 | tuple_rows.append(tup) 46 | index += 3 47 | # shuffle the tuple 48 | 49 | random.shuffle(tuple_rows) 50 | print("The number of num_skill is ", max_skill_num+1) 51 | print("Finish reading data...") 52 | self.max_len, self.num_skill = max_num_problems, max_skill_num + 1 53 | return tuple_rows 54 | 55 | def get_train_examples(self, data_dir): 56 | """See base class.""" 57 | 58 | return self._create_examples( 59 | self._read_tsv(data_dir)) 60 | 61 | def get_dev_examples(self, data_dir): 62 | """See base class.""" 63 | return self._create_examples( 64 | self._read_tsv(data_dir)) 65 | 66 | # def get_test_examples(self, data_dir): 67 | # """See base class.""" 68 | # return self._create_examples( 69 | # self._read_tsv(os.path.join(data_dir, "test_matched.tsv")), "test") 70 | 71 | def _create_examples(self, tuple_rows): 72 | """Creates examples for the training and dev sets.""" 73 | seq_len = [] 74 | inputs = [] 75 | target_correct = [] 76 | target_id = [] 77 | ids = [] 78 | correct = [] 79 | for i in range(len(tuple_rows)): 80 | # inputs 81 | inputs.append([int(tuple_rows[i][1][j]) + int(tuple_rows[i][2][j]) * self.num_skill for j in range(len(tuple_rows[i][1]) - 1)]) 82 | seq_len.append(int(tuple_rows[i][0][0]) - 1) # sequence 83 | target_id.append(list(map(lambda k: int(k), tuple_rows[i][1][1:]))) 84 | target_correct.append(list(map(lambda k: int(k), tuple_rows[i][2][1:]))) 85 | 86 | ids.append(list(map(lambda k: int(k), tuple_rows[i][1][:-1]))) 87 | correct.append(list(map(lambda k: int(k), tuple_rows[i][2][:-1]))) 88 | 89 | return np.array(inputs), np.array(target_id), np.array(target_correct), np.array(ids), np.array(correct), np.array(seq_len) 90 | 91 | 92 | def encode_tfrecord(tfrecords_filename, inputs, target_id, target_correct, ids, correct, seq_len): 93 | writer = tf.python_io.TFRecordWriter(tfrecords_filename) 94 | nums = inputs.shape[0] 95 | print('%s records' % nums) 96 | for i in range(nums): 97 | tfrecords_features = dict() 98 | 99 | tfrecords_features['inputs'] = tf.train.Feature(float_list=tf.train.FloatList(value=inputs[i])) 100 | tfrecords_features['target_id'] = tf.train.Feature(float_list=tf.train.FloatList(value=target_id[i])) 101 | tfrecords_features['target_correct'] = tf.train.Feature(int64_list=tf.train.Int64List(value=target_correct[i])) 102 | tfrecords_features['ids'] = tf.train.Feature(float_list=tf.train.FloatList(value=ids[i])) 103 | tfrecords_features['correct'] = tf.train.Feature(int64_list=tf.train.Int64List(value=target_id[i])) 104 | tfrecords_features['seq_len'] = tf.train.Feature(int64_list=tf.train.Int64List(value=[seq_len[i]])) 105 | example = tf.train.Example(features=tf.train.Features(feature=tfrecords_features)) 106 | exmp_serial = example.SerializeToString() 107 | writer.write(exmp_serial) 108 | writer.close() 109 | 110 | 111 | if __name__ == '__main__': 112 | 113 | train_dir = '../data/2016-EDM/0910_b_train.csv' 114 | processor = EDM_Processor() 115 | inputs, target_id, target_correct, ids, correct, seq_len = processor.get_train_examples(train_dir) 116 | print('data shape:', inputs.shape, target_id.shape, target_correct.shape, seq_len.shape) 117 | 118 | encode_tfrecord('train.tfrecord', inputs, target_id, target_correct, ids, correct, seq_len) 119 | 120 | train_dir = '../data/2016-EDM/0910_b_test.csv' 121 | processor = EDM_Processor() 122 | inputs, target_id, target_correct, ids, correct, seq_len = processor.get_train_examples(train_dir) 123 | print('data shape:', inputs.shape, target_id.shape, target_correct.shape, seq_len.shape) 124 | encode_tfrecord('eval.tfrecord', inputs, target_id, target_correct, ids, correct, seq_len) 125 | -------------------------------------------------------------------------------- /code/data/eval.tfrecord: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sulingling123/Knowledge_Tracing/e47425c068066190c0a435dc1ba1ce2c2a9c56ec/code/data/eval.tfrecord -------------------------------------------------------------------------------- /code/data/train.tfrecord: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sulingling123/Knowledge_Tracing/e47425c068066190c0a435dc1ba1ce2c2a9c56ec/code/data/train.tfrecord --------------------------------------------------------------------------------