def conditional_probability_correct(abilities, ex_parameters, exercise_ind): """Predict the probabilities of getting questions correct for a set of exercise indices, given model parameters in couplings and the abilities vector for the user. Args: abilities: An ndarray with shape = (a, 1), where a = the number of abilities in the model (not including the bias) couplings: An ndarray with shape (n, a + 1), where n = the number of exercises known by the model. execises_ind: An ndarray of exercise indices in the coupling matrix. The argument specifies which exercises the caller would like conditional probabilities for. Should be 1-d with shape = (# of exercises queried for) Returns: An ndarray of floats with shape = (exercise_ind.size) """ # Pad the abilities vector with a 1 to act as a bias. # The shape of abilities will become (a+1, 1). abilities = np.append(abilities.copy(), np.ones((1, 1)), axis=0) difficulties = ex_parameters.W_correct[exercise_ind, :] Z = sigmoid(np.dot(difficulties, abilities)) Z = np.reshape(Z, Z.size) # flatten to 1-d ndarray return Z
def L_dL_singleuser(arg): """Calculate log likelihood and gradient wrt couplings of mIRT model for single user """ theta, state, options = arg abilities = state['abilities'].copy() correct = state['correct'] exercises_ind = state['exercises_ind'] dL = Parameters(theta.num_abilities, theta.num_exercises) # pad the abilities vector with a 1 to act as a bias abilities = np.append(abilities.copy(), np.ones((1, abilities.shape[1])), axis=0) # the abilities to exercise coupling parameters for this exercise W_correct = theta.W_correct[exercises_ind, :] # calculate the probability of getting a question in this exercise correct Y = np.dot(W_correct, abilities) Z = sigmoid(Y) # predicted correctness value Zt = correct.reshape(Z.shape) # true correctness value pdata = Zt * Z + (1. - Zt) * (1. - Z) # = 2*Zt*Z - Z + const dLdY = ((2. * Zt - 1.) * Z * (1. - Z)) / pdata L = -np.sum(np.log(pdata)) dL.W_correct = -np.dot(dLdY, abilities.T) if not options.correct_only: # calculate the probability of taking time response_time to answer log_time_taken = state['log_time_taken'] # the abilities to time coupling parameters for this exercise W_time = theta.W_time[exercises_ind, :] sigma = theta.sigma_time[exercises_ind].reshape((-1, 1)) Y = np.dot(W_time, abilities) err = (Y - log_time_taken.reshape((-1, 1))) L += np.sum(err**2 / sigma**2) / 2. dLdY = err / sigma**2 dL.W_time = np.dot(dLdY, abilities.T) dL.sigma_time = (-err**2 / sigma**3).ravel() # normalization for the Gaussian L += np.sum(0.5 * np.log(sigma**2)) dL.sigma_time += 1. / sigma.ravel() return L, dL, exercises_ind
def L_dL_singleuser(arg): """Calculate log likelihood and gradient wrt couplings of mIRT model for single user """ theta, state, options = arg abilities = state['abilities'].copy() correct = state['correct'] exercises_ind = state['exercises_ind'] dL = Parameters(theta.num_abilities, theta.num_exercises) # pad the abilities vector with a 1 to act as a bias abilities = np.append(abilities.copy(), np.ones((1, abilities.shape[1])), axis=0) # the abilities to exercise coupling parameters for this exercise W_correct = theta.W_correct[exercises_ind, :] # calculate the probability of getting a question in this exercise correct Y = np.dot(W_correct, abilities) Z = sigmoid(Y) # predicted correctness value Zt = correct.reshape(Z.shape) # true correctness value pdata = Zt * Z + (1. - Zt) * (1. - Z) # = 2*Zt*Z - Z + const dLdY = ((2. * Zt - 1.) * Z * (1. - Z)) / pdata L = -np.sum(np.log(pdata)) dL.W_correct = -np.dot(dLdY, abilities.T) if not options.correct_only: # calculate the probability of taking time response_time to answer log_time_taken = state['log_time_taken'] # the abilities to time coupling parameters for this exercise W_time = theta.W_time[exercises_ind, :] sigma = theta.sigma_time[exercises_ind].reshape((-1, 1)) Y = np.dot(W_time, abilities) err = (Y - log_time_taken.reshape((-1, 1))) L += np.sum(err ** 2 / sigma ** 2) / 2. dLdY = err / sigma ** 2 dL.W_time = np.dot(dLdY, abilities.T) dL.sigma_time = (-err ** 2 / sigma ** 3).ravel() # normalization for the Gaussian L += np.sum(0.5 * np.log(sigma ** 2)) dL.sigma_time += 1. / sigma.ravel() return L, dL, exercises_ind