55module 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