Skip to content

Commit

Permalink
Add Three-Parameter IRT Model with improvements (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
kholdrex authored Jun 9, 2024
1 parent d2e8906 commit 0196dc3
Showing 1 changed file with 13 additions and 10 deletions.
23 changes: 13 additions & 10 deletions lib/irt_ruby/three_parameter_model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,50 +5,52 @@
module IrtRuby
# A class representing the Three-Parameter model for Item Response Theory.
class ThreeParameterModel
def initialize(data, max_iter: 1000, tolerance: 1e-6)
def initialize(data, max_iter: 1000, tolerance: 1e-6, learning_rate: 0.01)
@data = data
@abilities = Array.new(data.row_count) { rand }
@difficulties = Array.new(data.column_count) { rand }
@discriminations = Array.new(data.column_count) { rand }
@guessings = Array.new(data.column_count) { rand * 0.3 }
@max_iter = max_iter
@tolerance = tolerance
@learning_rate = learning_rate
end

# Sigmoid function to calculate probability
def sigmoid(x)
1.0 / (1.0 + Math.exp(-x))
end

# Probability function for the 3PL model
def probability(theta, a, b, c)
c + (1 - c) * sigmoid(a * (theta - b))
end

# Calculate the log-likelihood of the data given the current parameters
def likelihood
likelihood = 0
@data.row_vectors.each_with_index do |row, i|
row.to_a.each_with_index do |response, j|
prob = probability(@abilities[i], @discriminations[j], @difficulties[j], @guessings[j])
if response == 1
likelihood += Math.log(prob)
elsif response.zero?
likelihood += Math.log(1 - prob)
end
likelihood += response == 1 ? Math.log(prob) : Math.log(1 - prob)
end
end
likelihood
end

# Update parameters using gradient ascent
def update_parameters
last_likelihood = likelihood
@max_iter.times do |_iter|
@data.row_vectors.each_with_index do |row, i|
row.to_a.each_with_index do |response, j|
prob = probability(@abilities[i], @discriminations[j], @difficulties[j], @guessings[j])
error = response - prob
@abilities[i] += 0.01 * error * @discriminations[j]
@difficulties[j] -= 0.01 * error * @discriminations[j]
@discriminations[j] += 0.01 * error * (@abilities[i] - @difficulties[j])
@guessings[j] += 0.01 * error * (1 - prob)
@abilities[i] += @learning_rate * error * @discriminations[j]
@difficulties[j] -= @learning_rate * error * @discriminations[j]
@discriminations[j] += @learning_rate * error * (@abilities[i] - @difficulties[j])
@guessings[j] += @learning_rate * error * (1 - prob)
@guessings[j] = [[@guessings[j], 0].max, 1].min # Keep guessings within [0, 1]
end
end
current_likelihood = likelihood
Expand All @@ -58,6 +60,7 @@ def update_parameters
end
end

# Fit the model to the data
def fit
update_parameters
{
Expand Down

0 comments on commit 0196dc3

Please sign in to comment.