Skip to content

Commit 0196dc3

Browse files
authored
Add Three-Parameter IRT Model with improvements (#8)
1 parent d2e8906 commit 0196dc3

1 file changed

Lines changed: 13 additions & 10 deletions

File tree

lib/irt_ruby/three_parameter_model.rb

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,50 +5,52 @@
55
module IrtRuby
66
# A class representing the Three-Parameter model for Item Response Theory.
77
class ThreeParameterModel
8-
def initialize(data, max_iter: 1000, tolerance: 1e-6)
8+
def initialize(data, max_iter: 1000, tolerance: 1e-6, learning_rate: 0.01)
99
@data = data
1010
@abilities = Array.new(data.row_count) { rand }
1111
@difficulties = Array.new(data.column_count) { rand }
1212
@discriminations = Array.new(data.column_count) { rand }
1313
@guessings = Array.new(data.column_count) { rand * 0.3 }
1414
@max_iter = max_iter
1515
@tolerance = tolerance
16+
@learning_rate = learning_rate
1617
end
1718

19+
# Sigmoid function to calculate probability
1820
def sigmoid(x)
1921
1.0 / (1.0 + Math.exp(-x))
2022
end
2123

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

29+
# Calculate the log-likelihood of the data given the current parameters
2630
def likelihood
2731
likelihood = 0
2832
@data.row_vectors.each_with_index do |row, i|
2933
row.to_a.each_with_index do |response, j|
3034
prob = probability(@abilities[i], @discriminations[j], @difficulties[j], @guessings[j])
31-
if response == 1
32-
likelihood += Math.log(prob)
33-
elsif response.zero?
34-
likelihood += Math.log(1 - prob)
35-
end
35+
likelihood += response == 1 ? Math.log(prob) : Math.log(1 - prob)
3636
end
3737
end
3838
likelihood
3939
end
4040

41+
# Update parameters using gradient ascent
4142
def update_parameters
4243
last_likelihood = likelihood
4344
@max_iter.times do |_iter|
4445
@data.row_vectors.each_with_index do |row, i|
4546
row.to_a.each_with_index do |response, j|
4647
prob = probability(@abilities[i], @discriminations[j], @difficulties[j], @guessings[j])
4748
error = response - prob
48-
@abilities[i] += 0.01 * error * @discriminations[j]
49-
@difficulties[j] -= 0.01 * error * @discriminations[j]
50-
@discriminations[j] += 0.01 * error * (@abilities[i] - @difficulties[j])
51-
@guessings[j] += 0.01 * error * (1 - prob)
49+
@abilities[i] += @learning_rate * error * @discriminations[j]
50+
@difficulties[j] -= @learning_rate * error * @discriminations[j]
51+
@discriminations[j] += @learning_rate * error * (@abilities[i] - @difficulties[j])
52+
@guessings[j] += @learning_rate * error * (1 - prob)
53+
@guessings[j] = [[@guessings[j], 0].max, 1].min # Keep guessings within [0, 1]
5254
end
5355
end
5456
current_likelihood = likelihood
@@ -58,6 +60,7 @@ def update_parameters
5860
end
5961
end
6062

63+
# Fit the model to the data
6164
def fit
6265
update_parameters
6366
{

0 commit comments

Comments
 (0)