nlu_adapter 0.1.2 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.yardopts +8 -0
- data/CHANGELOG.md +12 -0
- data/README.md +16 -4
- data/lib/nlu_adapter.rb +2 -0
- data/lib/nlu_adapter/dialogflow.rb +55 -1
- data/lib/nlu_adapter/intent.rb +7 -1
- data/lib/nlu_adapter/intent_collection.rb +8 -1
- data/lib/nlu_adapter/lex.rb +54 -10
- data/lib/nlu_adapter/metrics.rb +178 -0
- data/lib/nlu_adapter/parse_helper.rb +100 -0
- data/lib/nlu_adapter/version.rb +1 -1
- data/nlu_adapter.gemspec +6 -4
- metadata +27 -10
- data/Gemfile.lock +0 -94
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7499fe30c4727e84ae33be8b0e3905d921942daa4b74cac9105105e473107160
|
4
|
+
data.tar.gz: 2cdb76a1ba9593b53d80d5f32b3be140c38d629e376c9abbd6893ff1ae1669d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d0f35e5f09313ba18410c8ccf9d07ae4ea4ecb6cb217cf348e0cfccb8af0600e01262911e0890a706360b0c650ad9a9f395cf86273af9fa541065dec0098f854
|
7
|
+
data.tar.gz: 1d350b886b3154d973a5aef113cd505311ecc544037f7250f2f7a5da0b1691e6e87b7c9b91ed6c51fd116527c7207a1c78585705944cc0c566067007fdc7d606
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Gemfile.lock
|
data/.yardopts
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
0.1.4
|
2
|
+
---
|
3
|
+
* Fixed a bad push
|
4
|
+
|
5
|
+
0.1.3
|
6
|
+
---
|
7
|
+
* New functionalities:
|
8
|
+
* bulk_parse - parse multiple texts
|
9
|
+
* parse_test - test multiple texts & intents and get the resuts in json/csv/ruby hash formats
|
10
|
+
* parse_test_report - gives a test report summary with metrics: accuracy, confusion_matrix, class wise precision, recall and total
|
11
|
+
* More documentation
|
12
|
+
|
1
13
|
0.1.2 (31/Dec/2018)
|
2
14
|
---
|
3
15
|
* fixed lib path error
|
data/README.md
CHANGED
@@ -23,9 +23,21 @@ Or install it yourself as:
|
|
23
23
|
|
24
24
|
Check [documentation](docs) for usage.
|
25
25
|
|
26
|
-
##
|
27
|
-
|
28
|
-
|
26
|
+
## NLU Adapter support matrix
|
27
|
+
|
28
|
+
C: Create, R: Get, U: Update, D: Delete
|
29
|
+
|
30
|
+
NLU Service | Intent | Intet Collection | Intent identification
|
31
|
+
----------- | ------ | ---------------- | ---------------------
|
32
|
+
[Aws Lex](https://aws.amazon.com/lex/) ([doc](docs/lex.md)) | C,R,U | C,R,U | Yes
|
33
|
+
[Google Dialogflow](https://dialogflow.com/) ([doc](docs/dialogflow.md)) | C,R,U | To-Do | Yes
|
34
|
+
|
35
|
+
## Classification metrics
|
36
|
+
### Supported intent classification metrics
|
37
|
+
* Accuracy
|
38
|
+
* Confusion Matrix
|
39
|
+
* Precision (per Class)
|
40
|
+
* Recall (per Class)
|
29
41
|
|
30
42
|
## Development
|
31
43
|
|
@@ -35,7 +47,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
35
47
|
|
36
48
|
## Contributing
|
37
49
|
|
38
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/technopreneurG/
|
50
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/technopreneurG/nlu_adapter_ruby. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
39
51
|
|
40
52
|
## License
|
41
53
|
|
data/lib/nlu_adapter.rb
CHANGED
@@ -3,6 +3,8 @@ require "nlu_adapter/version"
|
|
3
3
|
module NluAdapter
|
4
4
|
autoload :NluAdapterIntent, 'nlu_adapter/intent'
|
5
5
|
autoload :NluAdapterIntentCollection, 'nlu_adapter/intent_collection'
|
6
|
+
autoload :ParseHelper, 'nlu_adapter/parse_helper'
|
7
|
+
autoload :Metrics, 'nlu_adapter/metrics'
|
6
8
|
module Adapters
|
7
9
|
autoload :Lex, 'nlu_adapter/lex'
|
8
10
|
autoload :Dialogflow, 'nlu_adapter/dialogflow'
|
@@ -2,21 +2,37 @@ require "google/cloud/dialogflow"
|
|
2
2
|
|
3
3
|
module NluAdapter
|
4
4
|
module Adapters
|
5
|
+
# Dialogflow wrapper class
|
6
|
+
#
|
5
7
|
class Dialogflow
|
8
|
+
include ParseHelper
|
9
|
+
|
10
|
+
# Constructor
|
6
11
|
def initialize(options = {})
|
7
12
|
@project_id = options[:project_id]
|
8
13
|
@session_id = options[:session_id]
|
9
14
|
end
|
10
15
|
|
11
|
-
|
16
|
+
# Understand a given text
|
17
|
+
#
|
18
|
+
# @param text [String] a text to parse using the NLU provider
|
19
|
+
# @return [Json] return the identified intent name
|
20
|
+
#
|
21
|
+
def parse(text)
|
12
22
|
sessions_client = Google::Cloud::Dialogflow::Sessions.new(version: :v2)
|
13
23
|
formatted_session = Google::Cloud::Dialogflow::V2::SessionsClient.session_path(@project_id, @session_id)
|
24
|
+
language_code = 'en'
|
14
25
|
query_input = Google::Cloud::Dialogflow::V2::QueryInput.new({text: {language_code: language_code, text: text}})
|
15
26
|
response = sessions_client.detect_intent(formatted_session, query_input)
|
16
27
|
|
17
28
|
return { intent_name: response.query_result.intent.display_name }
|
18
29
|
end
|
19
30
|
|
31
|
+
# Get an instance of Intent, if it exists else nil
|
32
|
+
#
|
33
|
+
# @param name [String] name of the intent
|
34
|
+
# @return [Intent] intent object
|
35
|
+
#
|
20
36
|
def get_intent(name)
|
21
37
|
intents_client = Google::Cloud::Dialogflow::Intents.new(version: :v2)
|
22
38
|
formatted_parent = Google::Cloud::Dialogflow::V2::IntentsClient.project_agent_path(@project_id)
|
@@ -31,6 +47,12 @@ module NluAdapter
|
|
31
47
|
return nil
|
32
48
|
end
|
33
49
|
|
50
|
+
# Get a new instance of Intent
|
51
|
+
#
|
52
|
+
# @param name [String] name of the intent
|
53
|
+
# @param utterences [Array] phrases for training
|
54
|
+
# @return [Intent] Intent object
|
55
|
+
#
|
34
56
|
def new_intent(name, utterences = [])
|
35
57
|
i = get_intent(name)
|
36
58
|
|
@@ -40,6 +62,13 @@ module NluAdapter
|
|
40
62
|
return i
|
41
63
|
end
|
42
64
|
|
65
|
+
# Given an Intent object, create/update it in Dialogflow
|
66
|
+
#
|
67
|
+
# @param intent [Intent] Intent object
|
68
|
+
# @return [Intent] Intent object
|
69
|
+
#
|
70
|
+
# @todo convert response -> Intent
|
71
|
+
#
|
43
72
|
def create_intent(intent)
|
44
73
|
intents_client = Google::Cloud::Dialogflow::Intents.new(version: :v2)
|
45
74
|
formatted_parent = Google::Cloud::Dialogflow::V2::IntentsClient.project_agent_path(@project_id)
|
@@ -55,25 +84,39 @@ module NluAdapter
|
|
55
84
|
end
|
56
85
|
end
|
57
86
|
|
87
|
+
# Not implemented
|
88
|
+
# @todo check back
|
89
|
+
#
|
58
90
|
def get_intent_collection(name)
|
59
91
|
end
|
60
92
|
|
93
|
+
# Not implemented
|
94
|
+
# @todo check back
|
95
|
+
#
|
61
96
|
def new_intent_collection(name, intents)
|
62
97
|
end
|
63
98
|
|
99
|
+
# Not implemented
|
100
|
+
# @todo check back
|
101
|
+
#
|
64
102
|
def create_intent_collection(collection)
|
65
103
|
end
|
66
104
|
|
105
|
+
# Class represents Intent in an IntentCollection
|
67
106
|
class Intent
|
68
107
|
include NluAdapterIntent
|
69
108
|
attr_accessor :id
|
70
109
|
|
110
|
+
# Constructor
|
71
111
|
def initialize(options = {})
|
72
112
|
@name = options[:name] #DF.intent.display_name
|
73
113
|
@id = options[:id] #DF.intent.name
|
74
114
|
@utterences = options[:utterences]
|
75
115
|
end
|
76
116
|
|
117
|
+
# Convert self to Hash
|
118
|
+
# @return [Hash] ruby hash
|
119
|
+
#
|
77
120
|
def to_h
|
78
121
|
training_phrases = []
|
79
122
|
@utterences.each do |u|
|
@@ -87,21 +130,32 @@ module NluAdapter
|
|
87
130
|
}
|
88
131
|
end
|
89
132
|
|
133
|
+
# convert self to json
|
134
|
+
# @return [json] json
|
135
|
+
#
|
90
136
|
def to_json
|
91
137
|
to_h.to_json
|
92
138
|
end
|
93
139
|
|
94
140
|
end
|
95
141
|
|
142
|
+
# Class represents a collection of Intents
|
96
143
|
class IntentCollection
|
97
144
|
include NluAdapterIntentCollection
|
98
145
|
|
146
|
+
# Constructor
|
99
147
|
def initialize(options = {})
|
100
148
|
end
|
101
149
|
|
150
|
+
# Convert self to Hash
|
151
|
+
# @return [Hash] ruby hash
|
152
|
+
#
|
102
153
|
def to_h
|
103
154
|
end
|
104
155
|
|
156
|
+
# convert self to json
|
157
|
+
# @return [json] json
|
158
|
+
#
|
105
159
|
def to_json
|
106
160
|
end
|
107
161
|
end
|
data/lib/nlu_adapter/intent.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
require 'json'
|
2
2
|
|
3
|
-
#Class represents Intent in an IntentCollection
|
3
|
+
# Class represents Intent in an IntentCollection
|
4
4
|
module NluAdapterIntent
|
5
5
|
attr_accessor :name, :utterences
|
6
6
|
|
7
|
+
# Convert self to Hash
|
8
|
+
# @return [Hash] ruby hash
|
9
|
+
#
|
7
10
|
def to_h
|
8
11
|
{
|
9
12
|
:name => @name,
|
@@ -11,6 +14,9 @@ module NluAdapterIntent
|
|
11
14
|
}
|
12
15
|
end
|
13
16
|
|
17
|
+
# Convert self to Json
|
18
|
+
# @return [Json] json
|
19
|
+
#
|
14
20
|
def to_json
|
15
21
|
to_h.to_json
|
16
22
|
end
|
@@ -1,14 +1,18 @@
|
|
1
1
|
require 'json'
|
2
2
|
|
3
|
-
#Class represents a collection of Intents
|
3
|
+
# Class represents a collection of Intents
|
4
4
|
module NluAdapterIntentCollection
|
5
5
|
attr_accessor :name, :intents
|
6
6
|
|
7
|
+
# Constructor
|
7
8
|
def initialize(name, intents)
|
8
9
|
@name = name
|
9
10
|
@intents = intents
|
10
11
|
end
|
11
12
|
|
13
|
+
# Convert self to Hash
|
14
|
+
# @return [Hash] ruby hash
|
15
|
+
#
|
12
16
|
def to_h
|
13
17
|
{
|
14
18
|
:name => @name,
|
@@ -16,6 +20,9 @@ module NluAdapterIntentCollection
|
|
16
20
|
}
|
17
21
|
end
|
18
22
|
|
23
|
+
# Convert self to Json
|
24
|
+
# @return [Json] json
|
25
|
+
#
|
19
26
|
def to_json
|
20
27
|
to_h.to_json
|
21
28
|
end
|
data/lib/nlu_adapter/lex.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
|
2
1
|
require 'aws-sdk-lex'
|
3
2
|
require 'aws-sdk-lexmodelbuildingservice'
|
4
3
|
|
5
4
|
module NluAdapter
|
6
5
|
module Adapters
|
6
|
+
# AWS Lex wrapper class
|
7
7
|
class Lex
|
8
|
+
include ParseHelper
|
8
9
|
|
10
|
+
# Constructor
|
9
11
|
def initialize(options = {})
|
10
12
|
#creds & region from config/env
|
11
13
|
@bot_name = options[:bot_name]
|
@@ -15,7 +17,11 @@ module NluAdapter
|
|
15
17
|
@lexm_client = Aws::LexModelBuildingService::Client.new()
|
16
18
|
end
|
17
19
|
|
18
|
-
#
|
20
|
+
# Understand a given text
|
21
|
+
#
|
22
|
+
# @param text [String] a text to parse using the NLU provider
|
23
|
+
# @return [Json] return the identified intent name
|
24
|
+
#
|
19
25
|
def parse(text)
|
20
26
|
intent_name = nil
|
21
27
|
begin
|
@@ -39,7 +45,12 @@ module NluAdapter
|
|
39
45
|
return { intent_name: intent_name }
|
40
46
|
end
|
41
47
|
|
42
|
-
#
|
48
|
+
# Get an instance of Intent, if it exists else nil
|
49
|
+
#
|
50
|
+
# @param name [String] name of the intent
|
51
|
+
# @param version [String] version of the intent
|
52
|
+
# @return [Intent] intent object
|
53
|
+
#
|
43
54
|
def get_intent(name, version = nil)
|
44
55
|
version = '$LATEST'
|
45
56
|
|
@@ -53,7 +64,12 @@ module NluAdapter
|
|
53
64
|
return nil
|
54
65
|
end
|
55
66
|
|
56
|
-
#
|
67
|
+
# Get a new instance of Intent
|
68
|
+
#
|
69
|
+
# @param name [String] name of the intent
|
70
|
+
# @param utterences [Array] phrases for training
|
71
|
+
# @return [Intent] Intent object
|
72
|
+
#
|
57
73
|
def new_intent(name, utterences= [])
|
58
74
|
#check for existing intent, and get checksum
|
59
75
|
|
@@ -65,12 +81,20 @@ module NluAdapter
|
|
65
81
|
return i
|
66
82
|
end
|
67
83
|
|
68
|
-
#
|
84
|
+
# Given an Intent object, create/update it in Lex
|
85
|
+
#
|
86
|
+
# @param intent [Intent] Intent object
|
87
|
+
# @return [Intent] Intent object
|
88
|
+
#
|
89
|
+
# @todo convert response -> Intent
|
90
|
+
#
|
69
91
|
def create_intent(intent)
|
70
92
|
resp = @lexm_client.put_intent(intent.to_h)
|
71
93
|
end
|
72
94
|
|
73
|
-
#
|
95
|
+
# Get an instance of IntentCollection, if it exists
|
96
|
+
# @param name [String] intent collection name
|
97
|
+
# @param version [String] version
|
74
98
|
def get_intent_collection(name, version = nil)
|
75
99
|
version = '$LATEST'
|
76
100
|
begin
|
@@ -83,7 +107,9 @@ module NluAdapter
|
|
83
107
|
return nil
|
84
108
|
end
|
85
109
|
|
86
|
-
#
|
110
|
+
# Get a new instance of Intent
|
111
|
+
# @param name [String] intent collection name
|
112
|
+
# @param intents [Array<Intent>] array of intent objects
|
87
113
|
def new_intent_collection(name, intents)
|
88
114
|
ic = get_intent_collection(name)
|
89
115
|
|
@@ -93,7 +119,10 @@ module NluAdapter
|
|
93
119
|
return ic
|
94
120
|
end
|
95
121
|
|
96
|
-
#
|
122
|
+
# Given an IntentCollection object, create it in Lex
|
123
|
+
#
|
124
|
+
# @param collection [IntentCollection] the Bot to be created
|
125
|
+
#
|
97
126
|
def create_intent_collection(collection)
|
98
127
|
#create/update intents
|
99
128
|
collection.intents.each do |i|
|
@@ -103,15 +132,20 @@ module NluAdapter
|
|
103
132
|
resp = @lexm_client.put_bot(collection.to_h)
|
104
133
|
end
|
105
134
|
|
106
|
-
#
|
135
|
+
# Class represents Intent in an IntentCollection
|
107
136
|
class Intent
|
108
137
|
include NluAdapterIntent
|
138
|
+
|
139
|
+
# Constructor
|
109
140
|
def initialize(options = {})
|
110
141
|
@name = options[:name]
|
111
142
|
@version = options[:version]
|
112
143
|
@checksum = options[:checksum]
|
113
144
|
end
|
114
145
|
|
146
|
+
# Convert self to Hash
|
147
|
+
# @return [Hash] ruby hash
|
148
|
+
#
|
115
149
|
def to_h
|
116
150
|
{
|
117
151
|
name: @name,
|
@@ -123,22 +157,29 @@ module NluAdapter
|
|
123
157
|
}
|
124
158
|
end
|
125
159
|
|
160
|
+
# convert self to Json
|
161
|
+
# @return [json] json
|
162
|
+
#
|
126
163
|
def to_json
|
127
164
|
to_h.to_json
|
128
165
|
end
|
129
166
|
end
|
130
167
|
|
131
|
-
#
|
168
|
+
# Class represents a collection of Intents
|
132
169
|
class IntentCollection
|
133
170
|
include NluAdapterIntentCollection
|
134
171
|
attr_accessor :extra
|
135
172
|
|
173
|
+
# Constructor
|
136
174
|
def initialize(options = {})
|
137
175
|
@name = options[:name]
|
138
176
|
@checksum = options[:checksum]
|
139
177
|
@intents = options[:intents]
|
140
178
|
end
|
141
179
|
|
180
|
+
# Convert self to Hash
|
181
|
+
# @return [Hash] ruby hash
|
182
|
+
#
|
142
183
|
def to_h
|
143
184
|
intents = []
|
144
185
|
@intents.each do |i|
|
@@ -183,6 +224,9 @@ module NluAdapter
|
|
183
224
|
}
|
184
225
|
end
|
185
226
|
|
227
|
+
# Convert self to Json
|
228
|
+
# @return [Json] json
|
229
|
+
#
|
186
230
|
def to_json
|
187
231
|
to_h.to_json
|
188
232
|
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
require 'matrix'
|
2
|
+
|
3
|
+
module NluAdapter
|
4
|
+
|
5
|
+
# A utility class to calculate classification matrics
|
6
|
+
# @see https://scikit-learn.org/stable/modules/model_evaluation.html#classification-metrics
|
7
|
+
#
|
8
|
+
class Metrics
|
9
|
+
|
10
|
+
# Constructor
|
11
|
+
# @param actual [Array] an array of actual results (true values)
|
12
|
+
# @param predicted [Array] an array of predicted results (predicted values)
|
13
|
+
# @param class_labels [Array] an array of class_labels for the results,
|
14
|
+
# this is required if actual & predicted values are numeric
|
15
|
+
#
|
16
|
+
def initialize(actual, predicted, class_labels = [])
|
17
|
+
@actual = actual
|
18
|
+
@predicted = predicted
|
19
|
+
@class_labels = class_labels
|
20
|
+
|
21
|
+
@actual.map!{ |a| a.is_a?(String)? a.intern : a }
|
22
|
+
@predicted.map!{ |p| p.is_a?(String)? p.intern : p }
|
23
|
+
@class_labels.map!{ |l| l.is_a?(String)? l.intern : l }
|
24
|
+
end
|
25
|
+
|
26
|
+
# Caclulate the accuracy
|
27
|
+
# @return [Float] accuracy
|
28
|
+
# @see https://en.wikipedia.org/wiki/Accuracy_and_precision
|
29
|
+
#
|
30
|
+
def accuracy
|
31
|
+
if @actual.size != @predicted.size
|
32
|
+
#todo: throw error
|
33
|
+
puts "actual & predicted array are not of same size"
|
34
|
+
return
|
35
|
+
end
|
36
|
+
|
37
|
+
total = @actual.size
|
38
|
+
correct = 0
|
39
|
+
@actual.each_with_index do |v, i|
|
40
|
+
correct+=1 if @predicted[i] == v
|
41
|
+
end
|
42
|
+
(correct.fdiv(total) * 100).round(4)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Get the confusion matrix
|
46
|
+
# @return [Matrix] confusion matrix
|
47
|
+
# @see https://en.wikipedia.org/wiki/Confusion_matrix
|
48
|
+
#
|
49
|
+
def confusion_matrix
|
50
|
+
class_labels = @class_labels
|
51
|
+
actual = @actual
|
52
|
+
predicted = @predicted
|
53
|
+
|
54
|
+
#if no class_labels, convert to numeric values, and extract the class_labels
|
55
|
+
if @class_labels.empty?
|
56
|
+
class_labels = (@actual + @predicted).sort.uniq.sort
|
57
|
+
class_labels.map!{|c| c.intern}
|
58
|
+
@actual.each_with_index do |v, i|
|
59
|
+
actual[i] = class_labels.index(v)
|
60
|
+
end
|
61
|
+
|
62
|
+
@predicted.each_with_index do |v, i|
|
63
|
+
predicted[i] = class_labels.index(v)
|
64
|
+
end
|
65
|
+
else
|
66
|
+
#check if any string passed in actual/predicted
|
67
|
+
i = actual.select { |a| a.is_a?(Symbol) }.size
|
68
|
+
j = predicted.select { |p| p.is_a?(Symbol) }.size
|
69
|
+
|
70
|
+
if i > 0 || j > 0
|
71
|
+
#todo: fix it OR throw error
|
72
|
+
puts "actual, predicted & class_labels array having string values not implemented yet"
|
73
|
+
return
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
m = Matrix.zero(class_labels.size)
|
79
|
+
|
80
|
+
@class_totals = Hash[class_labels.collect { |c| [c, 0] }]
|
81
|
+
actual.each_with_index do |vi, i|
|
82
|
+
vj = predicted[i]
|
83
|
+
m[vi, vj] = m[vi, vj] + 1
|
84
|
+
@class_totals[class_labels[vi]] += 1
|
85
|
+
end
|
86
|
+
|
87
|
+
@class_labels = class_labels
|
88
|
+
@actual = actual
|
89
|
+
@predicted = predicted
|
90
|
+
|
91
|
+
@m = m
|
92
|
+
return m
|
93
|
+
end
|
94
|
+
|
95
|
+
# Get totals of actual values per class
|
96
|
+
# @return [Hash] Hash of class totals
|
97
|
+
#
|
98
|
+
def class_totals
|
99
|
+
return @class_totals
|
100
|
+
end
|
101
|
+
|
102
|
+
# Get total positives
|
103
|
+
# @param class_name [String] class name
|
104
|
+
# @return [Integer] total positive value for class_name
|
105
|
+
#
|
106
|
+
def tp(class_name)
|
107
|
+
i = @class_labels.index(class_name.intern)
|
108
|
+
return nil if i == nil || @m == nil || @m.empty?
|
109
|
+
tp = @m[i, i]
|
110
|
+
return tp
|
111
|
+
end
|
112
|
+
|
113
|
+
# Get false positive
|
114
|
+
# @param class_name [String] class name
|
115
|
+
# @return [Integer] false positive value for class_name
|
116
|
+
#
|
117
|
+
def fp(class_name)
|
118
|
+
i = @class_labels.index(class_name.intern)
|
119
|
+
return nil if i == nil || @m == nil || @m.empty?
|
120
|
+
fp = @m.column(i).sum - tp(class_name)
|
121
|
+
return fp
|
122
|
+
end
|
123
|
+
|
124
|
+
# Get false negative
|
125
|
+
# @param class_name [String] class name
|
126
|
+
# @return [Integer] false negative value for class_name
|
127
|
+
#
|
128
|
+
def fn(class_name)
|
129
|
+
i = @class_labels.index(class_name.intern)
|
130
|
+
return nil if i == nil || @m == nil || @m.empty?
|
131
|
+
fn = @m.row(i).sum - tp(class_name)
|
132
|
+
return fn
|
133
|
+
end
|
134
|
+
|
135
|
+
# Get the precision for given class
|
136
|
+
# @param class_name [String] class name
|
137
|
+
# @return [Float] precision rounded off to 4 decimal places
|
138
|
+
#
|
139
|
+
def precision(class_name)
|
140
|
+
confusion_matrix if !@m
|
141
|
+
return 0.00 if tp(class_name) == 0
|
142
|
+
precision = tp(class_name).fdiv((tp(class_name) + fp(class_name))).round(4)
|
143
|
+
return precision
|
144
|
+
end
|
145
|
+
|
146
|
+
# Get the recall for given class
|
147
|
+
# @param class_name [String] class name
|
148
|
+
# @return [Float] recall rounded off to 4 decimal places
|
149
|
+
#
|
150
|
+
def recall(class_name)
|
151
|
+
confusion_matrix if !@m
|
152
|
+
return 0.00 if tp(class_name) == 0
|
153
|
+
recall = tp(class_name).fdiv((tp(class_name) + fn(class_name))).round(4)
|
154
|
+
|
155
|
+
return recall
|
156
|
+
end
|
157
|
+
|
158
|
+
# Generate classification report
|
159
|
+
# @return [Hash] precision, recall and totals for each class name as key
|
160
|
+
#
|
161
|
+
def classification_report
|
162
|
+
confusion_matrix if !@m
|
163
|
+
report = {}
|
164
|
+
@class_labels.each do |label|
|
165
|
+
report[label] = {
|
166
|
+
precision: precision(label),
|
167
|
+
recall: recall(label),
|
168
|
+
class_total: class_totals[label]
|
169
|
+
}
|
170
|
+
end
|
171
|
+
|
172
|
+
return report
|
173
|
+
end
|
174
|
+
|
175
|
+
#todo: f1-score
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'csv'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
#helper functions to parse text and match intent
|
5
|
+
module NluAdapter
|
6
|
+
module ParseHelper
|
7
|
+
# parse multiple texts
|
8
|
+
#
|
9
|
+
# Example input/output
|
10
|
+
# texts:
|
11
|
+
# ["book me a hotel, please", "book a hotel"]
|
12
|
+
# result:
|
13
|
+
# {"book me a hotel, please"=>"BookHotel", "book a hotel"=>"BookHotel"}
|
14
|
+
#
|
15
|
+
# @param [Array<String>] texts An array of texts to be parsed
|
16
|
+
# @return [Hash] return a hash of "text" => "result"
|
17
|
+
# @todo parallize
|
18
|
+
def bulk_parse(texts)
|
19
|
+
result = {}
|
20
|
+
texts.each do |text|
|
21
|
+
resp = parse(text)
|
22
|
+
result[text] = resp[:intent_name]
|
23
|
+
end
|
24
|
+
result
|
25
|
+
end
|
26
|
+
|
27
|
+
# run the parse tests with test_data and generate test_results in supported format
|
28
|
+
#
|
29
|
+
# Example input/output
|
30
|
+
# test_data
|
31
|
+
# {"BookHotel":["please book a hotel"],"intent1":["book me a hotel, please","book a hotel"]}
|
32
|
+
# test_results
|
33
|
+
# json:
|
34
|
+
# [{"text":"book me a hotel, please","expected":"intent1","got":"BookHotel"},{"text":"book a hotel","expected":"intent1","got":"BookHotel"},{"text":"please book a hotel","expected":"BookHotel","got":"BookHotel"}]
|
35
|
+
# csv:
|
36
|
+
# "book me a hotel, please",intent1,BookHotel
|
37
|
+
# book a hotel,intent1,BookHotel
|
38
|
+
# please book a hotel,BookHotel,BookHotel
|
39
|
+
# hash:
|
40
|
+
# [{:text=>"book me a hotel, please", :expected=>"intent1", :got=>"BookHotel"}, {:text=>"book a hotel", :expected=>"intent1", :got=>"BookHotel"}, {:text=>"please book a hotel", :expected=>"BookHotel", :got=>"BookHotel"}]
|
41
|
+
#
|
42
|
+
# @param test_data [Json]: Test data in specified format
|
43
|
+
# @param output_format [Symbol] supported formats :csv, :json or :hash
|
44
|
+
# @return [test_results]: output the test results in expected format
|
45
|
+
#
|
46
|
+
def parse_test(test_data, output_format = :hash)
|
47
|
+
test_results = []
|
48
|
+
test_data.each do |intent_name, texts|
|
49
|
+
resp = bulk_parse(texts)
|
50
|
+
texts.each do |t|
|
51
|
+
test_results << {text: t, expected: intent_name, got: resp[t]}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
case output_format
|
56
|
+
when :json
|
57
|
+
return test_results.to_json
|
58
|
+
when :csv
|
59
|
+
return to_csv(test_results)
|
60
|
+
when :hash
|
61
|
+
return test_results
|
62
|
+
else
|
63
|
+
puts 'Warning: valid format not specified'
|
64
|
+
return test_results
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
# run the parse tests with test_data and generate test report
|
71
|
+
#
|
72
|
+
# @param test_data [Json]: Test data in specified format
|
73
|
+
# @return [test_report]: generate a test report
|
74
|
+
# @todo F1-Score
|
75
|
+
#
|
76
|
+
def parse_test_report(test_data)
|
77
|
+
test_results = parse_test(test_data)
|
78
|
+
expected = []
|
79
|
+
got = []
|
80
|
+
test_results.each do |result|
|
81
|
+
expected << result[:expected]
|
82
|
+
got << result[:got]
|
83
|
+
end
|
84
|
+
|
85
|
+
m = Metrics.new(expected, got)
|
86
|
+
|
87
|
+
return {accuracy: m.accuracy, confusion_matrix: m.confusion_matrix, classification_report: m.classification_report}
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
def to_csv(test_results)
|
92
|
+
csv_string = CSV.generate do |csv|
|
93
|
+
test_results.each do |result|
|
94
|
+
csv << result.values
|
95
|
+
end
|
96
|
+
end
|
97
|
+
csv_string
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/lib/nlu_adapter/version.rb
CHANGED
data/nlu_adapter.gemspec
CHANGED
@@ -6,7 +6,7 @@ require "nlu_adapter/version"
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "nlu_adapter"
|
8
8
|
spec.version = NluAdapter::VERSION
|
9
|
-
spec.authors = ["Girish"]
|
9
|
+
spec.authors = ["Girish Nair"]
|
10
10
|
spec.email = ["getgirish@gmail.com"]
|
11
11
|
|
12
12
|
spec.summary = %q{NLU Adapter - An adapter for various NLU web services}
|
@@ -41,10 +41,12 @@ Gem::Specification.new do |spec|
|
|
41
41
|
spec.add_development_dependency "rspec", "~> 3.8"
|
42
42
|
|
43
43
|
#Official AWS Ruby gem for Amazon Lex Runtime Service. This gem is part of the AWS SDK for Ruby.
|
44
|
-
spec.add_dependency('aws-sdk-lex', '1.9')
|
45
|
-
spec.add_dependency('aws-sdk-lexmodelbuildingservice', '1.12')
|
44
|
+
spec.add_dependency('aws-sdk-lex', '~> 1.9')
|
45
|
+
spec.add_dependency('aws-sdk-lexmodelbuildingservice', '~> 1.12')
|
46
46
|
|
47
47
|
#google-cloud-dialogflow is the official library for Dialogflow API.
|
48
|
-
spec.add_dependency('google-cloud-dialogflow', '0.2.3')
|
48
|
+
spec.add_dependency('google-cloud-dialogflow', '~> 0.2.3')
|
49
49
|
|
50
|
+
#An implementation of Matrix and Vector classes.
|
51
|
+
spec.add_dependency('matrix', '~> 0.1.0')
|
50
52
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nlu_adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- Girish
|
7
|
+
- Girish Nair
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-01-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -56,44 +56,58 @@ dependencies:
|
|
56
56
|
name: aws-sdk-lex
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '1.9'
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '1.9'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: aws-sdk-lexmodelbuildingservice
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- -
|
73
|
+
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '1.12'
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- -
|
80
|
+
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '1.12'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: google-cloud-dialogflow
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- -
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: 0.2.3
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- -
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 0.2.3
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: matrix
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 0.1.0
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 0.1.0
|
97
111
|
description: An adapter for various NLU web services like Aws Lex, Google Dialogflow
|
98
112
|
etc.
|
99
113
|
email:
|
@@ -102,12 +116,13 @@ executables: []
|
|
102
116
|
extensions: []
|
103
117
|
extra_rdoc_files: []
|
104
118
|
files:
|
119
|
+
- ".gitignore"
|
105
120
|
- ".rspec"
|
121
|
+
- ".yardopts"
|
106
122
|
- CHANGELOG.md
|
107
123
|
- CODE_OF_CONDUCT.md
|
108
124
|
- Dockerfile
|
109
125
|
- Gemfile
|
110
|
-
- Gemfile.lock
|
111
126
|
- LICENSE
|
112
127
|
- README.md
|
113
128
|
- Rakefile
|
@@ -121,6 +136,8 @@ files:
|
|
121
136
|
- lib/nlu_adapter/intent.rb
|
122
137
|
- lib/nlu_adapter/intent_collection.rb
|
123
138
|
- lib/nlu_adapter/lex.rb
|
139
|
+
- lib/nlu_adapter/metrics.rb
|
140
|
+
- lib/nlu_adapter/parse_helper.rb
|
124
141
|
- lib/nlu_adapter/version.rb
|
125
142
|
- nlu_adapter.gemspec
|
126
143
|
homepage: https://rubygems.org/gems/nlu_adapter
|
data/Gemfile.lock
DELETED
@@ -1,94 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
nlu_adapter (0.1.2)
|
5
|
-
aws-sdk-lex (= 1.9)
|
6
|
-
aws-sdk-lexmodelbuildingservice (= 1.12)
|
7
|
-
google-cloud-dialogflow (= 0.2.3)
|
8
|
-
|
9
|
-
GEM
|
10
|
-
remote: https://rubygems.org/
|
11
|
-
specs:
|
12
|
-
addressable (2.5.2)
|
13
|
-
public_suffix (>= 2.0.2, < 4.0)
|
14
|
-
aws-eventstream (1.0.1)
|
15
|
-
aws-partitions (1.127.0)
|
16
|
-
aws-sdk-core (3.44.1)
|
17
|
-
aws-eventstream (~> 1.0)
|
18
|
-
aws-partitions (~> 1.0)
|
19
|
-
aws-sigv4 (~> 1.0)
|
20
|
-
jmespath (~> 1.0)
|
21
|
-
aws-sdk-lex (1.9.0)
|
22
|
-
aws-sdk-core (~> 3, >= 3.39.0)
|
23
|
-
aws-sigv4 (~> 1.0)
|
24
|
-
aws-sdk-lexmodelbuildingservice (1.12.0)
|
25
|
-
aws-sdk-core (~> 3, >= 3.39.0)
|
26
|
-
aws-sigv4 (~> 1.0)
|
27
|
-
aws-sigv4 (1.0.3)
|
28
|
-
diff-lcs (1.3)
|
29
|
-
faraday (0.15.4)
|
30
|
-
multipart-post (>= 1.2, < 3)
|
31
|
-
google-cloud-dialogflow (0.2.3)
|
32
|
-
google-gax (~> 1.3)
|
33
|
-
google-gax (1.4.0)
|
34
|
-
google-protobuf (~> 3.2)
|
35
|
-
googleapis-common-protos (>= 1.3.5, < 2.0)
|
36
|
-
googleauth (~> 0.6.2)
|
37
|
-
grpc (>= 1.7.2, < 2.0)
|
38
|
-
rly (~> 0.2.3)
|
39
|
-
google-protobuf (3.6.1)
|
40
|
-
googleapis-common-protos (1.3.7)
|
41
|
-
google-protobuf (~> 3.0)
|
42
|
-
googleapis-common-protos-types (~> 1.0)
|
43
|
-
grpc (~> 1.0)
|
44
|
-
googleapis-common-protos-types (1.0.2)
|
45
|
-
google-protobuf (~> 3.0)
|
46
|
-
googleauth (0.6.7)
|
47
|
-
faraday (~> 0.12)
|
48
|
-
jwt (>= 1.4, < 3.0)
|
49
|
-
memoist (~> 0.16)
|
50
|
-
multi_json (~> 1.11)
|
51
|
-
os (>= 0.9, < 2.0)
|
52
|
-
signet (~> 0.7)
|
53
|
-
grpc (1.17.1)
|
54
|
-
google-protobuf (~> 3.1)
|
55
|
-
googleapis-common-protos-types (~> 1.0.0)
|
56
|
-
jmespath (1.4.0)
|
57
|
-
jwt (2.1.0)
|
58
|
-
memoist (0.16.0)
|
59
|
-
multi_json (1.13.1)
|
60
|
-
multipart-post (2.0.0)
|
61
|
-
os (1.0.0)
|
62
|
-
public_suffix (3.0.3)
|
63
|
-
rake (12.3.2)
|
64
|
-
rly (0.2.3)
|
65
|
-
rspec (3.8.0)
|
66
|
-
rspec-core (~> 3.8.0)
|
67
|
-
rspec-expectations (~> 3.8.0)
|
68
|
-
rspec-mocks (~> 3.8.0)
|
69
|
-
rspec-core (3.8.0)
|
70
|
-
rspec-support (~> 3.8.0)
|
71
|
-
rspec-expectations (3.8.2)
|
72
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
73
|
-
rspec-support (~> 3.8.0)
|
74
|
-
rspec-mocks (3.8.0)
|
75
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
76
|
-
rspec-support (~> 3.8.0)
|
77
|
-
rspec-support (3.8.0)
|
78
|
-
signet (0.11.0)
|
79
|
-
addressable (~> 2.3)
|
80
|
-
faraday (~> 0.9)
|
81
|
-
jwt (>= 1.5, < 3.0)
|
82
|
-
multi_json (~> 1.10)
|
83
|
-
|
84
|
-
PLATFORMS
|
85
|
-
ruby
|
86
|
-
|
87
|
-
DEPENDENCIES
|
88
|
-
bundler (~> 1.16)
|
89
|
-
nlu_adapter!
|
90
|
-
rake (~> 12.0)
|
91
|
-
rspec (~> 3.8)
|
92
|
-
|
93
|
-
BUNDLED WITH
|
94
|
-
1.16.6
|