nlu_adapter 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|