Skip to content

Commit

Permalink
Merge f4a143d into a435235
Browse files Browse the repository at this point in the history
  • Loading branch information
bmjoseph committed Aug 8, 2019
2 parents a435235 + f4a143d commit c90c1b9
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 1 deletion.
6 changes: 6 additions & 0 deletions lib/scoruby/model_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require 'scoruby/models/gradient_boosted_model/model'
require 'scoruby/models/random_forest/model'
require 'scoruby/models/naive_bayes/model'
require 'scoruby/models/logistic_regression/model'

module Scoruby
class ModelFactory
Expand All @@ -17,10 +18,15 @@ def self.factory_for(xml)
return Models::GradientBoostedModel::Model.new(xml) if gbm?(xml)
return Models::DecisionTree.new(xml.child) if decision_tree?(xml)
return Models::NaiveBayes::Model.new(xml) if naive_bayes?(xml)
return Models::LogisticRegression::Model.new(xml) if logistic_regression?(xml)

raise MODEL_NOT_SUPPORTED_ERROR
end

def self.logistic_regression?(xml)
!xml.xpath('PMML/GeneralRegressionModel').empty?
end

def self.naive_bayes?(xml)
!xml.xpath('PMML/NaiveBayesModel').empty?
end
Expand Down
2 changes: 1 addition & 1 deletion lib/scoruby/models/decision_tree.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def decide(features)
while curr.children[0]
prev = curr
curr = step(curr, features)
return if didnt_step?(curr, prev)
break if didnt_step?(curr, prev)
end

curr.decision
Expand Down
28 changes: 28 additions & 0 deletions lib/scoruby/models/logistic_regression/data.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# frozen_string_literal: true

module Scoruby
module Models
module LogisticRegression
class Data
COEFFICIENTS_VALUES_PATH = '//PMML/GeneralRegressionModel/ParamMatrix/PCell/@beta'
COEFFICIENTS_LABELS_PATH = '//PMML/GeneralRegressionModel/ParameterList/Parameter/@label'

def initialize(xml)
@xml = xml
end

def coefficient_values
@xml.xpath(COEFFICIENTS_VALUES_PATH).map{|attribute| attribute.value.to_f}
end

def coefficient_labels
@xml.xpath(COEFFICIENTS_LABELS_PATH).map(&:value)
end

def coefficients
@coefficients ||= Hash[coefficient_labels.zip(coefficient_values)]
end
end
end
end
end
32 changes: 32 additions & 0 deletions lib/scoruby/models/logistic_regression/model.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

require 'scoruby/models/logistic_regression/data'
require 'forwardable'

module Scoruby
module Models
module LogisticRegression
class Model
extend Forwardable
def_delegators :@data, :coefficients, :coefficient_values

def initialize(xml)
@data = Data.new(xml)
end

def intercept
coefficient_values.first
end

def score(features)
logodds = intercept
features.each do |key, value|
logodds += coefficients[key.to_s] * value
end

1.0 / (1.0 + Math.exp(-logodds))
end
end
end
end
end
43 changes: 43 additions & 0 deletions spec/fixtures/logistic_regression.pmml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--(Comment generated by ADAPA) PMML processed by ADAPA (Version : 4.3)-->
<PMML version="4.3" xmlns="http://www.dmg.org/PMML-4_3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.dmg.org/PMML-4_3 http://www.dmg.org/pmml/v4-3/pmml-4-3.xsd">
<Header copyright="Copyright (c) 2019 baileyjoseph" description="Generalized Linear Regression Model">
<Extension name="user" value="baileyjoseph" extender="SoftwareAG PMML Generator"/>
<Application name="SoftwareAG PMML Generator" version="1.4"/>
<Timestamp>2019-08-07 16:16:51</Timestamp>
</Header>
<DataDictionary numberOfFields="3">
<DataField name="value" optype="continuous" dataType="double"/>
<DataField name="prob" optype="continuous" dataType="double"/>
<DataField name="noise_var" optype="continuous" dataType="double"/>
</DataDictionary>
<GeneralRegressionModel modelName="General_Regression_Model" modelType="generalizedLinear" functionName="regression" algorithmName="glm" distribution="binomial" linkFunction="logit">
<MiningSchema>
<MiningField name="value" usageType="predicted" invalidValueTreatment="returnInvalid"/>
<MiningField name="prob" usageType="active" invalidValueTreatment="returnInvalid"/>
<MiningField name="noise_var" usageType="active" invalidValueTreatment="returnInvalid"/>
</MiningSchema>
<Output>
<OutputField name="Predicted_value" feature="predictedValue" optype="continuous" dataType="double"/>
</Output>
<ParameterList>
<Parameter name="p0" label="(Intercept)"/>
<Parameter name="p1" label="prob"/>
<Parameter name="p2" label="noise_var"/>
</ParameterList>
<FactorList/>
<CovariateList>
<Predictor name="prob"/>
<Predictor name="noise_var"/>
</CovariateList>
<PPMatrix>
<PPCell value="1" predictorName="prob" parameterName="p1"/>
<PPCell value="1" predictorName="noise_var" parameterName="p2"/>
</PPMatrix>
<ParamMatrix>
<PCell parameterName="p0" df="1" beta="-2.88882584455418"/>
<PCell parameterName="p1" df="1" beta="6.08116002712647"/>
<PCell parameterName="p2" df="1" beta="-0.622996648302913"/>
</ParamMatrix>
</GeneralRegressionModel>
</PMML>
9 changes: 9 additions & 0 deletions spec/scoruby/model_factory_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,13 @@
expect(naive_bayes_model).to be_a Scoruby::Models::NaiveBayes::Model
end
end

context 'logistic_regression pmml' do
let(:logistic_regression_file) { 'spec/fixtures/logistic_regression.pmml' }
let(:logistic_regression_model) { Scoruby.load_model(logistic_regression_file) }

it 'loads LogisticRegression' do
expect(logistic_regression_model).to be_a Scoruby::Models::LogisticRegression::Model
end
end
end
26 changes: 26 additions & 0 deletions spec/scoruby/models/logistic_regression/data_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

require 'spec_helper'

describe Scoruby::Models::LogisticRegression::Data do
let(:xml) { Scoruby.xml_from_file_path(logistic_regression_file) }
let(:logistic_regression_file) { 'spec/fixtures/logistic_regression.pmml' }
let(:data) { described_class.new(xml) }

# it 'loads correct number of trees' do
# expect(data.decision_trees.count).to eq 15
# end
#
it 'loads correct number of coefficient values' do
expect(data.coefficient_values.count).to eq 3
end

it 'loads correct number of coefficient labels' do
expect(data.coefficient_labels.count).to eq 3
end

it 'loads correct number of coefficients' do
puts data.coefficients
expect(data.coefficients.count).to eq 3
end
end
21 changes: 21 additions & 0 deletions spec/scoruby/models/logistic_regression/model_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

require 'spec_helper'

describe Scoruby::Models::LogisticRegression::Model do
let(:logistic_regression_file) { 'spec/fixtures/logistic_regression.pmml' }
let(:xml) { Scoruby.xml_from_file_path(logistic_regression_file) }
let(:logistic_regression) { described_class.new(xml) }
let(:features) do
{
'prob' => 0.13,
'noise_var' => 0.5
}
end

context 'default' do
it 'scores features' do
expect(logistic_regression.score(features).round(8)).to eq 0.08243046
end
end
end

0 comments on commit c90c1b9

Please sign in to comment.