smalltext 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +0 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +45 -0
- data/LICENSE.txt +21 -0
- data/README.md +148 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/intent_class.nn +110 -0
- data/lib/smalltext.rb +348 -0
- data/lib/smalltext/version.rb +3 -0
- data/smalltext.gemspec +48 -0
- metadata +172 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4073482150c4b9864de23d5a8c4665c388e94c2b657de026e0bb9d29399110b3
|
4
|
+
data.tar.gz: ca5bc424871f2a3fe76b9fc81a8b0538108292c5f6fd41896d31c006ac7e4fda
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6247ce3a178fc8d660ead9e4e92a6e3321f0c78019494a7f47c1d8fe7227c831ed39ec1c30519c4dd32045dd8941202c8048d3dcd83c32a853e444bbf0d24599
|
7
|
+
data.tar.gz: 3013217a0bf23d751206bb71acc94f0c0e57d4c3636bb68a55f65fd9e5e114f7e944f4a68103d7b62d56933726902a209d9324b711ed346bfd410bb183be6f55
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
File without changes
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at arjunmenon009@gmail.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
smalltext (0.1.0)
|
5
|
+
croupier
|
6
|
+
numo-narray (~> 0.9.1.3)
|
7
|
+
porter2stemmer
|
8
|
+
rambling-trie
|
9
|
+
tokenizer
|
10
|
+
|
11
|
+
GEM
|
12
|
+
remote: https://rubygems.org/
|
13
|
+
specs:
|
14
|
+
croupier (1.6.0)
|
15
|
+
diff-lcs (1.3)
|
16
|
+
numo-narray (0.9.1.3)
|
17
|
+
porter2stemmer (1.0.1)
|
18
|
+
rake (10.4.2)
|
19
|
+
rambling-trie (2.0.0)
|
20
|
+
rspec (3.8.0)
|
21
|
+
rspec-core (~> 3.8.0)
|
22
|
+
rspec-expectations (~> 3.8.0)
|
23
|
+
rspec-mocks (~> 3.8.0)
|
24
|
+
rspec-core (3.8.0)
|
25
|
+
rspec-support (~> 3.8.0)
|
26
|
+
rspec-expectations (3.8.2)
|
27
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
28
|
+
rspec-support (~> 3.8.0)
|
29
|
+
rspec-mocks (3.8.0)
|
30
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
31
|
+
rspec-support (~> 3.8.0)
|
32
|
+
rspec-support (3.8.0)
|
33
|
+
tokenizer (0.3.0)
|
34
|
+
|
35
|
+
PLATFORMS
|
36
|
+
x86-mingw32
|
37
|
+
|
38
|
+
DEPENDENCIES
|
39
|
+
bundler (~> 1.17)
|
40
|
+
rake (~> 10.0)
|
41
|
+
rspec (~> 3.0)
|
42
|
+
smalltext!
|
43
|
+
|
44
|
+
BUNDLED WITH
|
45
|
+
1.17.1
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 arjun
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
# Smalltext
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/smalltext`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
Classify short texts with neural network.
|
6
|
+
|
7
|
+
This gem is specifically created to classify small sentence/datasets using a supervised training algorithm. You can use this in place of Naive Bayes.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'smalltext'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install smalltext
|
24
|
+
|
25
|
+
|
26
|
+
## Dependencies
|
27
|
+
|
28
|
+
Gem depends on Numo/NArray, Porter2Stemmer, Tokenizer, Croupier
|
29
|
+
|
30
|
+
Gem dependencies should be automatically installed.
|
31
|
+
|
32
|
+
## Usage
|
33
|
+
|
34
|
+
Classification is easy to get started.
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
require 'smalltext'
|
38
|
+
|
39
|
+
s = Smalltext::Classifier.new
|
40
|
+
|
41
|
+
# Add your sentence using the `add_item` method
|
42
|
+
# Classifier#add_item(category, sentence)
|
43
|
+
|
44
|
+
s.add_item("schedule_list", "What time is my next session?")
|
45
|
+
s.add_item("schedule_list", "When is my next session?")
|
46
|
+
s.add_item("schedule_list", "What time is my next meeting?")
|
47
|
+
s.add_item("schedule_list", "Can you please show me my schedule?")
|
48
|
+
s.add_item("schedule_list", "Show me my schedule.")
|
49
|
+
|
50
|
+
s.add_item("greetings", "Hi")
|
51
|
+
s.add_item("greetings", "How are you doing?")
|
52
|
+
s.add_item("greetings", "have a nice day")
|
53
|
+
s.add_item("greetings", "good morning.")
|
54
|
+
s.add_item("greetings", "Whats up")
|
55
|
+
s.add_item("greetings", "Yo")
|
56
|
+
|
57
|
+
s.add_item("where_is", "Where is narkel bagan")
|
58
|
+
s.add_item("where_is", "show me the way to sasta sundar")
|
59
|
+
s.add_item("where_is", "where is the staircase")
|
60
|
+
s.add_item("where_is", "give me the direction to the parking lot")
|
61
|
+
|
62
|
+
s.add_item("weather", "Whats the weather")
|
63
|
+
s.add_item("weather", "Weather in Noida")
|
64
|
+
s.add_item("weather", "Is it raining")
|
65
|
+
s.add_item("weather", "Will it be hot tomorrow in Mumbai")
|
66
|
+
s.add_item("weather", "What is the maximum temperature today")
|
67
|
+
|
68
|
+
s.add_item("finance", "How many dollars is 17 euros?")
|
69
|
+
s.add_item("finance", "How much is 100 ruppees in US dollars")
|
70
|
+
s.add_item("finance", "How much is Starbucks stock?")
|
71
|
+
s.add_item("finance", "Tell me bitcoin exchange rate")
|
72
|
+
s.add_item("finance", "What is the value of ruppee")
|
73
|
+
s.add_item("finance", "Share price of Microsoft")
|
74
|
+
|
75
|
+
# Train a model using the Classifier#train method
|
76
|
+
|
77
|
+
s.train
|
78
|
+
|
79
|
+
# Test your trained model using the CLassifier#classify method
|
80
|
+
|
81
|
+
s.classify("give me the direction to moon")
|
82
|
+
|
83
|
+
# sentence: give me the direction to moon
|
84
|
+
# classification: [["where_is", 1.0]]
|
85
|
+
|
86
|
+
# => [["where_is", 1.0]]
|
87
|
+
```
|
88
|
+
|
89
|
+
You can also save your model. Use the `Classifier#save_model(file_name)`
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
s.save_model('intents.model')
|
93
|
+
```
|
94
|
+
|
95
|
+
To load a saved model use the `Classifier#load_model(file_name)`
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
s.load_model('intents.model')
|
99
|
+
|
100
|
+
s.classify("when is the next meeting")
|
101
|
+
# sentence: when is the next meeting
|
102
|
+
# classification: [["schedule_list", 0.9999189960209529]]
|
103
|
+
|
104
|
+
# => [["schedule_list", 0.9999189960209529]]
|
105
|
+
```
|
106
|
+
|
107
|
+
|
108
|
+
## Development
|
109
|
+
|
110
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
111
|
+
|
112
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
113
|
+
|
114
|
+
## Contributing
|
115
|
+
|
116
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/smalltext. 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.
|
117
|
+
|
118
|
+
## License
|
119
|
+
|
120
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
121
|
+
|
122
|
+
## Code of Conduct
|
123
|
+
|
124
|
+
Everyone interacting in the Smalltext project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/smalltext/blob/master/CODE_OF_CONDUCT.md).
|
125
|
+
|
126
|
+
## Credits
|
127
|
+
|
128
|
+
This gem is a Ruby port to the [Medium article](https://machinelearnings.co/text-classification-using-neural-networks-f5cd7b8765c6) describing text classification in Python.
|
129
|
+
|
130
|
+
## Roadmap
|
131
|
+
|
132
|
+
Goal of this gem is be an efficient tool for short texts classifications, where dataset is a constraint.
|
133
|
+
|
134
|
+
- Implement word vectors.
|
135
|
+
When dataset is sparse, we should leverage word vectors to map in categorizing unseen words.
|
136
|
+
|
137
|
+
- More algorithm options
|
138
|
+
Apart from neural networks, one can also switch and compare with different algorithms which mostly suits their needs.
|
139
|
+
|
140
|
+
## Todo
|
141
|
+
|
142
|
+
- Write Tests
|
143
|
+
- Add support for batch training
|
144
|
+
- Add SVD
|
145
|
+
- Add support for word vectors. Need to try this [method](https://multithreaded.stitchfix.com/blog/2017/10/18/stop-using-word2vec/).
|
146
|
+
- Add more classification algorithms.
|
147
|
+
- Add benchmarking
|
148
|
+
- Create model for known text datasets like Reuters, etc.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "smalltext"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/intent_class.nn
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
{
|
2
|
+
:
|
3
|
+
f0.4596867910248106f-0.687203151043698f-0.36728361439776314f-3.1538590480746134f1.3263269682990184[
|
4
|
+
f-1.441564192764147f0.9217975408499933f0.8302022778271181f-0.5180306687561195f-3.004470511797579[
|
5
|
+
f0.47236702217871024f-3.2564378413816506f-1.8561125119153883f0.08451786720555526f1.2934286960690824[
|
6
|
+
f-2.181737541606334f-0.5389229549371255f-0.6259628967845187f-2.3464055062328377f3.0243446802135683[
|
7
|
+
f0.12288687993183259f-2.610821781363835f-0.3973043460305657f-0.4609412036673507f-2.6980255283737637[
|
8
|
+
f-1.9219785164280614f1.080137445888194f0.8208612852022767f-1.8451941095656226f-1.7084821950463362[
|
9
|
+
f1.8439922120737957f1.7916671770321728f-0.46147175436107324f-3.1115553900921626f-2.767234668912591[
|
10
|
+
f-1.4892002889702398f2.384699958844114f-2.7548135828227207f-1.5472458452892048f1.9066930687588495[
|
11
|
+
f0.32737881875760283f-2.132579854900912f1.1550193660791115f-2.61473413086656f-0.313751144367379[
|
12
|
+
f-2.0188941471699007f2.279793119816791f0.24495796159125974f-0.6772179624131887f-1.3138818100821106[
|
13
|
+
f-2.069659302493741f0.025186215541228928f-2.6570410244353795f3.014115443952716f0.23615678357962905[
|
14
|
+
f-1.4403177374845622f2.085005560730284f-2.299576301607141f-1.6459620509973663f0.9061646479109549[
|
15
|
+
f-0.45376843157965324f0.2907081542069082f-4.062012629404391f0.9896126571308395f-1.8498313928947558[
|
16
|
+
f1.9215274233934307f-0.7440102923765316f-1.584239441209481f-2.9332405518587414f0.021206362990984357[
|
17
|
+
f1.680950118427406f-1.3097722574120703f-1.8240015891679504f1.4555512139687008f-3.369205350049291[
|
18
|
+
f-1.4337178794614471f2.182479903865861f-2.424323888497003f1.105692100894958f-1.3801306140154412[
|
19
|
+
f-3.626276034322108f-2.569631015404794f-0.33488709479446555f0.011340989294128523f0.9785159342724172[
|
20
|
+
f-1.6593647190509841f-0.20287260615288705f2.1485252597004685f1.755321726875816f-1.6640694086967727[
|
21
|
+
f-1.0054052890058038f2.0162347363108766f2.856080333912819f-2.1206250172441647f-1.86218195788549[
|
22
|
+
f-2.618412013792327f-2.575294799242185f1.722379713892787f0.3059455320988714f-0.5009685814265834:
|
23
|
+
words[MI" what:
|
24
|
+
@fI"is;
|
25
|
+
@fI"my;
|
26
|
+
@fI" next;
|
27
|
+
@fI"session;
|
28
|
+
@fI" when;
|
29
|
+
@fI" meet;
|
30
|
+
@fI"can;
|
31
|
+
@fI"you;
|
32
|
+
@fI"
|
33
|
+
pleas;
|
34
|
+
@fI" show;
|
35
|
+
@fI"me;
|
36
|
+
@fI"schedul;
|
37
|
+
@fI".;
|
38
|
+
@fI"hi;
|
39
|
+
@fI"how;
|
40
|
+
@fI"are;
|
41
|
+
@fI"do;
|
42
|
+
@fI" have;
|
43
|
+
@fI"a;
|
44
|
+
@fI" nice;
|
45
|
+
@fI"day;
|
46
|
+
@fI" good;
|
47
|
+
@fI" morn;
|
48
|
+
@fI"up;
|
49
|
+
@fI"yo;
|
50
|
+
@fI"
|
51
|
+
where;
|
52
|
+
@fI"narkel;
|
53
|
+
@fI"
|
54
|
+
bagan;
|
55
|
+
@fI"the;
|
56
|
+
@fI"way;
|
57
|
+
@fI"to;
|
58
|
+
@fI"
|
59
|
+
sasta;
|
60
|
+
@fI"sundar;
|
61
|
+
@fI"
|
62
|
+
@fI" give;
|
63
|
+
@fI"direct;
|
64
|
+
@fI" park;
|
65
|
+
@fI"lot;
|
66
|
+
@fI"weather;
|
67
|
+
@fI"in;
|
68
|
+
@fI"
|
69
|
+
noida;
|
70
|
+
@fI"it;
|
71
|
+
@fI" rain;
|
72
|
+
@fI" will;
|
73
|
+
@fI"be;
|
74
|
+
@fI"hot;
|
75
|
+
@fI"
|
76
|
+
@fI"mumbai;
|
77
|
+
@fI"maximum;
|
78
|
+
@fI"temperatur;
|
79
|
+
@fI"
|
80
|
+
today;
|
81
|
+
@fI" mani;
|
82
|
+
@fI"dollar;
|
83
|
+
@fI"17;
|
84
|
+
@fI" euro;
|
85
|
+
@fI" much;
|
86
|
+
@fI"100;
|
87
|
+
@fI"
|
88
|
+
ruppe;
|
89
|
+
@fI"us;
|
90
|
+
@fI"
|
91
|
+
@fI"
|
92
|
+
stock;
|
93
|
+
@fI" tell;
|
94
|
+
@fI"bitcoin;
|
95
|
+
@fI"exchang;
|
96
|
+
@fI" rate;
|
97
|
+
@fI" valu;
|
98
|
+
@fI"of;
|
99
|
+
@fI"
|
100
|
+
share;
|
101
|
+
@fI"
|
102
|
+
price;
|
103
|
+
@fI"microsoft;
|
104
|
+
@f:klasses[
|
105
|
+
I"schedule_list;
|
106
|
+
@fI"greetings;
|
107
|
+
@fI"
|
108
|
+
@fI"weather;
|
109
|
+
@fI"finance;
|
110
|
+
@f
|
data/lib/smalltext.rb
ADDED
@@ -0,0 +1,348 @@
|
|
1
|
+
require 'porter2stemmer'
|
2
|
+
require 'tokenizer'
|
3
|
+
require 'numo/narray'
|
4
|
+
require "croupier"
|
5
|
+
require 'rambling-trie'
|
6
|
+
|
7
|
+
require "smalltext/version"
|
8
|
+
|
9
|
+
# probability threshold
|
10
|
+
ERROR_THRESHOLD = 0.2
|
11
|
+
|
12
|
+
module Smalltext
|
13
|
+
class Error < StandardError; end
|
14
|
+
# Your code goes here...
|
15
|
+
|
16
|
+
class Classifier
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@training_data = []
|
20
|
+
|
21
|
+
#organizing our data structures for documents , @categories, words
|
22
|
+
@ignore_words = ['?']
|
23
|
+
@words=[]
|
24
|
+
@categories=[]
|
25
|
+
@documents=[]
|
26
|
+
@tokenizer = Tokenizer::Tokenizer.new(:en)
|
27
|
+
|
28
|
+
#create our bow training data
|
29
|
+
@training=[]
|
30
|
+
@output=[]
|
31
|
+
@synapse = {}
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_item(category, sentence)
|
35
|
+
@training_data.push({"category":category, "sentence":sentence})
|
36
|
+
end
|
37
|
+
|
38
|
+
def train(hidden_neurons=20, alpha=0.1, epochs=1000, dropout=false, dropout_percent=0.2)
|
39
|
+
preprocess
|
40
|
+
x_inp = Numo::NArray[training][0,true,true]
|
41
|
+
y = Numo::NArray[output][0,true,true]
|
42
|
+
|
43
|
+
start_time = Time.now
|
44
|
+
|
45
|
+
neural_network(x_inp, y, hidden_neurons=hidden_neurons, alpha=alpha, epochs=epochs, dropout=dropout, dropout_percent=dropout_percent)
|
46
|
+
|
47
|
+
elapsed_time = Time.now - start_time
|
48
|
+
puts
|
49
|
+
puts
|
50
|
+
puts "Model training complete."
|
51
|
+
puts "Processing time: #{elapsed_time} seconds"
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
def classify(sentence, show_details=false)
|
56
|
+
results = think(sentence, show_details)
|
57
|
+
# puts "results is #{results.inspect}"
|
58
|
+
|
59
|
+
# results = [[i,r] for i,r in enumerate(results) if r>ERROR_THRESHOLD ]
|
60
|
+
results = results.to_a.map.each_with_index {|r,i| [i, r] if r > ERROR_THRESHOLD }.compact
|
61
|
+
# # results.sort(key=lambda x: x[1], reverse=True)
|
62
|
+
results.sort! {|a,b| b[1] <=> a[1] }
|
63
|
+
# return_results =[[classes[r[0]],r[1]] for r in results]
|
64
|
+
return_results = results.map {|r| [klasses[r[0]], r[1]] }
|
65
|
+
puts "sentence: #{sentence}\nclassification: #{return_results}"
|
66
|
+
puts
|
67
|
+
return return_results
|
68
|
+
end
|
69
|
+
|
70
|
+
def save_model(synapse_file)
|
71
|
+
synapse_file = synapse_file
|
72
|
+
|
73
|
+
unless @synapse.empty?
|
74
|
+
File.open(synapse_file, 'wb') do |file|
|
75
|
+
file.write(Marshal.dump(@synapse))
|
76
|
+
end
|
77
|
+
puts "saved synapses to: #{synapse_file}"
|
78
|
+
else
|
79
|
+
puts "Model not trained. Use the 'Classifier#train' method to build a model."
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def load_model(synapse_file)
|
84
|
+
@synapse = Marshal.load(File.binread(synapse_file))
|
85
|
+
@synapse[:synapse0] = Numo::NArray.cast(@synapse[:synapse0])
|
86
|
+
@synapse[:synapse1] = Numo::NArray.cast(@synapse[:synapse1])
|
87
|
+
|
88
|
+
@words = @synapse[:words]
|
89
|
+
@categories = @synapse[:klasses]
|
90
|
+
|
91
|
+
puts "Model #{synapse_file} loaded. Model was created on #{@synapse[:datetime]}"
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def preprocess
|
98
|
+
#loop through each sentence in our training data
|
99
|
+
@training_data.each do |pattern|
|
100
|
+
#tokenize in each word in the sentence
|
101
|
+
w = @tokenizer.tokenize(pattern[:sentence])
|
102
|
+
|
103
|
+
#add to our words list
|
104
|
+
@words += w
|
105
|
+
|
106
|
+
#add to documents in our corpus
|
107
|
+
@documents.push([w,pattern[:category]])
|
108
|
+
|
109
|
+
#add to our @categories list
|
110
|
+
if !@categories.include?(pattern[:category])
|
111
|
+
@categories.push(pattern[:category])
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
@ignore_words.each {|ign| @words.delete(ign) }
|
116
|
+
@words.map! {|word| word.stem }
|
117
|
+
@words.uniq!
|
118
|
+
@categories.uniq!
|
119
|
+
|
120
|
+
prepare_bow
|
121
|
+
end
|
122
|
+
|
123
|
+
def prepare_bow
|
124
|
+
#create an empty array for our output
|
125
|
+
output_empty = Array.new(@categories.size) { 0 }
|
126
|
+
|
127
|
+
#training set, bag of words for each sentence
|
128
|
+
@documents.each do |doc|
|
129
|
+
#initialize our bag of words
|
130
|
+
bag=[]
|
131
|
+
#list of tokenized words for the pattern
|
132
|
+
pattern_words=doc[0]
|
133
|
+
#stem each word
|
134
|
+
pattern_words.map! {|word| word.stem }
|
135
|
+
#create our bag of words array
|
136
|
+
@words.each { |w| if pattern_words.include?(w) then bag << 1 else bag << 0 end }
|
137
|
+
@training.push(bag)
|
138
|
+
#output is a 0 for each tag and 1 for current tag
|
139
|
+
# output_row = Array.new(output_empty)
|
140
|
+
output_row = output_empty.dup
|
141
|
+
output_row[@categories.index(doc[1])] = 1
|
142
|
+
@output << output_row
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def training
|
147
|
+
return @training
|
148
|
+
end
|
149
|
+
|
150
|
+
def output
|
151
|
+
return @output
|
152
|
+
end
|
153
|
+
|
154
|
+
def klasses
|
155
|
+
return @categories
|
156
|
+
end
|
157
|
+
|
158
|
+
def words
|
159
|
+
return @words
|
160
|
+
end
|
161
|
+
|
162
|
+
def clean_up_sentence(sentence)
|
163
|
+
#tokenize the pattern
|
164
|
+
sentence_words = @tokenizer.tokenize(sentence)
|
165
|
+
#stem each word
|
166
|
+
# sentence_words=[stemmer.stem(word.lower()) for word in sentence_words]
|
167
|
+
sentence_words.map! {|word| word.stem }
|
168
|
+
end
|
169
|
+
|
170
|
+
#return bag of words array: 0 or 1 for each word in the bag that exists in the sentence
|
171
|
+
def bow(sentence, words, show_details=false)
|
172
|
+
#tokenize the pattern
|
173
|
+
sentence_words=clean_up_sentence(sentence)
|
174
|
+
#bag of words
|
175
|
+
bag=[0] * words.size
|
176
|
+
# for s in sentence_words:
|
177
|
+
sentence_words.each do |s|
|
178
|
+
words.each_with_index do |w,i|
|
179
|
+
if w == s
|
180
|
+
bag[i] = 1
|
181
|
+
if show_details
|
182
|
+
puts "found in bag: #{w}"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
# return Numo::Narray.new(bag)
|
188
|
+
return Numo::DFloat[bag].flatten
|
189
|
+
end
|
190
|
+
|
191
|
+
def think(sentence, show_details=false)
|
192
|
+
x= bow(sentence.downcase, words,show_details)
|
193
|
+
if show_details
|
194
|
+
puts "sentence: #{sentence},\nbow: #{x}"
|
195
|
+
end
|
196
|
+
#input layer is our bag of words
|
197
|
+
l0=x
|
198
|
+
# matrix multiplication of input and hidden layer
|
199
|
+
l1 = sigmoid(l0.dot @synapse[:synapse0])
|
200
|
+
# l1 = softmax(l0.dot @synapse_0)
|
201
|
+
# output layer
|
202
|
+
# l2 = sigmoid(l1.dot @synapse_1)
|
203
|
+
l2 = softmax(l1.dot @synapse[:synapse1])
|
204
|
+
|
205
|
+
return l2
|
206
|
+
end
|
207
|
+
|
208
|
+
def neural_network(x_inp, y, hidden_neurons=10, alpha=1, epochs=50000, dropout=false, dropout_percent=0.5)
|
209
|
+
|
210
|
+
puts "Training with #{hidden_neurons} neurons, alpha:#{alpha}, dropout:#{dropout} #{dropout_percent if dropout}"
|
211
|
+
# puts x_inp.inspect
|
212
|
+
# puts "Input matrix: #{x_inp.size}x#{x_inp[0].size} Output matrix: #{1}x#{@categories.size}"
|
213
|
+
puts "Input matrix: #{x_inp.shape} Output matrix: #{1}x#{@categories.size}"
|
214
|
+
puts "Epochs set to #{epochs}. Every 100th iteration will be printed."
|
215
|
+
puts
|
216
|
+
|
217
|
+
last_mean_error = 1
|
218
|
+
# randomly initialize our weights with mean 0
|
219
|
+
# synapse_0 = 2*np.random.random((len(x_inp[0]), hidden_neurons)) - 1
|
220
|
+
synapse_0 = 2*Numo::DFloat.new(x_inp[0,true].size, hidden_neurons).rand - 1
|
221
|
+
# puts "synapse_0 is #{synapse_0.inspect}"
|
222
|
+
# synapse_1 = 2*np.random.random((hidden_neurons, len(@categories))) - 1
|
223
|
+
synapse_1 = 2*Numo::DFloat.new(hidden_neurons, @categories.size).rand - 1
|
224
|
+
|
225
|
+
|
226
|
+
prev_synapse_0_weight_update = synapse_0.new_zeros
|
227
|
+
prev_synapse_1_weight_update = synapse_1.new_zeros
|
228
|
+
|
229
|
+
synapse_0_direction_count = synapse_0.new_zeros
|
230
|
+
synapse_1_direction_count = synapse_1.new_zeros
|
231
|
+
|
232
|
+
(epochs + 1).times do |j|
|
233
|
+
# Feed forward through layers 0, 1, and 2
|
234
|
+
layer_0 = x_inp
|
235
|
+
# puts "synapse_0 in block is #{synapse_0.inspect}"
|
236
|
+
# puts "layer_0 is #{layer_0.inspect}"
|
237
|
+
layer_1 = sigmoid(layer_0.dot synapse_0)
|
238
|
+
# layer_1 = tanh(layer_0.dot synapse_0)
|
239
|
+
|
240
|
+
if dropout
|
241
|
+
# layer_1 *= np.random.binomial([np.ones((len(x_inp),hidden_neurons))],1-dropout_percent)[0] * (1.0/(1-dropout_percent))
|
242
|
+
b = Croupier::Distributions.binomial size: 1, success: (1-dropout_percent)
|
243
|
+
arr = Array.new(x_inp.size) { Array.new(hidden_neurons) {b.generate_number} }
|
244
|
+
layer_1 = Numo::DFloat[arr].reshape(x_inp.size,hidden_neurons) * (1.0/(1-dropout_percent))
|
245
|
+
end
|
246
|
+
|
247
|
+
layer_2 = sigmoid((layer_1.dot synapse_1))
|
248
|
+
# layer_2 = tanh((layer_1.dot synapse_1))
|
249
|
+
|
250
|
+
# how much did we miss the target value?
|
251
|
+
layer_2_error = y - layer_2
|
252
|
+
|
253
|
+
|
254
|
+
if (j% 10000) == 0 and j > 5000
|
255
|
+
# if this 10k iteration's error is greater than the last iteration, break out
|
256
|
+
if (layer_2_error.abs).mean < last_mean_error
|
257
|
+
puts "delta after #{j} iterations: #{(layer_2_error.abs).mean} )"
|
258
|
+
last_mean_error = (layer_2_error.abs).mean
|
259
|
+
else
|
260
|
+
puts "break: #{(layer_2_error.abs).mean} > #{last_mean_error}"
|
261
|
+
break
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
# in what direction is the target value?
|
266
|
+
# were we really sure? if so, don't change too much.
|
267
|
+
# layer_2_delta = layer_2_error * sigmoid_output_to_derivative(layer_2)
|
268
|
+
layer_2_delta = layer_2_error * dtanh(layer_2)
|
269
|
+
|
270
|
+
# how much did each l1 value contribute to the l2 error (according to the weights)?
|
271
|
+
layer_1_error = layer_2_delta.dot(synapse_1.transpose)
|
272
|
+
|
273
|
+
# in what direction is the target l1?
|
274
|
+
# were we really sure? if so, don't change too much.
|
275
|
+
# layer_1_delta = layer_1_error * sigmoid_output_to_derivative(layer_1)
|
276
|
+
layer_1_delta = layer_1_error * dtanh(layer_1)
|
277
|
+
|
278
|
+
synapse_1_weight_update = (layer_1.transpose).dot(layer_2_delta)
|
279
|
+
synapse_0_weight_update = (layer_0.transpose).dot(layer_1_delta)
|
280
|
+
|
281
|
+
|
282
|
+
if(j > 0)
|
283
|
+
# Bit array does not support arithmetic operation. Cast to Numo::Int32.cast, see https://github.com/ruby-numo/numo-narray/issues/65#issuecomment-323665534
|
284
|
+
# puts "synapse_0_direction_count",synapse_0_direction_count.inspect
|
285
|
+
# puts "synapse_0_weight_update", synapse_0_weight_update.inspect
|
286
|
+
# puts "prev_synapse_0_weight_update", prev_synapse_0_weight_update.inspect
|
287
|
+
synapse_0_direction_count += ( Numo::Int32.cast((synapse_0_weight_update > 0)) - Numo::Int32.cast((prev_synapse_0_weight_update > 0)) ).abs
|
288
|
+
synapse_1_direction_count += ( Numo::Int32.cast((synapse_1_weight_update > 0)) - Numo::Int32.cast((prev_synapse_1_weight_update > 0))).abs
|
289
|
+
end
|
290
|
+
|
291
|
+
synapse_1 += alpha * synapse_1_weight_update
|
292
|
+
synapse_0 += alpha * synapse_0_weight_update
|
293
|
+
|
294
|
+
prev_synapse_0_weight_update = synapse_0_weight_update
|
295
|
+
prev_synapse_1_weight_update = synapse_1_weight_update
|
296
|
+
print "."
|
297
|
+
if (j%100 == 0)
|
298
|
+
print j
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
now = Time.now
|
303
|
+
# puts "BEFORE DUMPING #{synapse_0.inspect}"
|
304
|
+
# persist synapses
|
305
|
+
@synapse = {'synapse0': synapse_0.to_a, 'synapse1': synapse_1.to_a,
|
306
|
+
'datetime': now.strftime("%Y-%m-%d %H:%M"),
|
307
|
+
'words': @words,
|
308
|
+
'klasses': @categories
|
309
|
+
}
|
310
|
+
|
311
|
+
# synapse_file = "intent_class.nn"
|
312
|
+
|
313
|
+
# File.open(synapse_file, 'wb') do |file|
|
314
|
+
# file.write(Marshal.dump(@synapse))
|
315
|
+
# end
|
316
|
+
# puts "saved synapses to: #{synapse_file}"
|
317
|
+
end
|
318
|
+
|
319
|
+
#compute sigmoid nonlinearity
|
320
|
+
def sigmoid(x)
|
321
|
+
output=1/(1+Numo::NMath.exp(-x))
|
322
|
+
end
|
323
|
+
#convert output of sigmoid function to its derivative
|
324
|
+
def sigmoid_output_to_derivative(output)
|
325
|
+
output*(1-output)
|
326
|
+
end
|
327
|
+
|
328
|
+
# using softmax as output layer is recommended for classification where outputs are mutually exclusive
|
329
|
+
def softmax(w)
|
330
|
+
e = Numo::NMath.exp(w - (w.max))
|
331
|
+
dist = e / (e.sum)
|
332
|
+
return dist
|
333
|
+
end
|
334
|
+
|
335
|
+
# using tanh over logistic sigmoid for the hidden layer is recommended
|
336
|
+
def tanh(x)
|
337
|
+
Numo::NMath.tanh(x)
|
338
|
+
end
|
339
|
+
|
340
|
+
# derivative for tanh sigmoid
|
341
|
+
def dtanh(y)
|
342
|
+
# 1 - y*y
|
343
|
+
return 1.0 - Numo::NMath.tanh(y)**2
|
344
|
+
end
|
345
|
+
|
346
|
+
end # END class
|
347
|
+
|
348
|
+
end
|
data/smalltext.gemspec
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "smalltext/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "smalltext"
|
8
|
+
spec.version = Smalltext::VERSION
|
9
|
+
spec.authors = ["arjun"]
|
10
|
+
spec.email = ["arjunmenon009@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Classify short texts with neural network}
|
13
|
+
spec.description = %q{Classify short texts with neural network}
|
14
|
+
spec.homepage = "https://www.github.com/arjunmenon/smalltext"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
# if spec.respond_to?(:metadata)
|
20
|
+
# spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
21
|
+
|
22
|
+
# spec.metadata["homepage_uri"] = spec.homepage
|
23
|
+
# spec.metadata["source_code_uri"] = "https://www.github.com/arjunmenon/smalltext"
|
24
|
+
# spec.metadata["changelog_uri"] = "https://www.github.com/arjunmenon/smalltext/CHANGELOG.md"
|
25
|
+
# else
|
26
|
+
# raise "RubyGems 2.0 or newer is required to protect against " \
|
27
|
+
# "public gem pushes."
|
28
|
+
# end
|
29
|
+
|
30
|
+
# Specify which files should be added to the gem when it is released.
|
31
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
32
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
33
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
34
|
+
end
|
35
|
+
spec.bindir = "exe"
|
36
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
37
|
+
spec.require_paths = ["lib"]
|
38
|
+
|
39
|
+
spec.add_runtime_dependency 'rambling-trie'
|
40
|
+
spec.add_runtime_dependency 'croupier'
|
41
|
+
spec.add_runtime_dependency 'numo-narray', '~> 0.9.1.3'
|
42
|
+
spec.add_runtime_dependency 'tokenizer'
|
43
|
+
spec.add_runtime_dependency 'porter2stemmer'
|
44
|
+
|
45
|
+
spec.add_development_dependency "bundler", "~> 1.17"
|
46
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
47
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: smalltext
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- arjun
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-11-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rambling-trie
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: croupier
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: numo-narray
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.9.1.3
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.9.1.3
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: tokenizer
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: porter2stemmer
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: bundler
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.17'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.17'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '10.0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '10.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rspec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '3.0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '3.0'
|
125
|
+
description: Classify short texts with neural network
|
126
|
+
email:
|
127
|
+
- arjunmenon009@gmail.com
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- ".gitignore"
|
133
|
+
- ".rspec"
|
134
|
+
- ".travis.yml"
|
135
|
+
- CHANGELOG.md
|
136
|
+
- CODE_OF_CONDUCT.md
|
137
|
+
- Gemfile
|
138
|
+
- Gemfile.lock
|
139
|
+
- LICENSE.txt
|
140
|
+
- README.md
|
141
|
+
- Rakefile
|
142
|
+
- bin/console
|
143
|
+
- bin/setup
|
144
|
+
- intent_class.nn
|
145
|
+
- lib/smalltext.rb
|
146
|
+
- lib/smalltext/version.rb
|
147
|
+
- smalltext.gemspec
|
148
|
+
homepage: https://www.github.com/arjunmenon/smalltext
|
149
|
+
licenses:
|
150
|
+
- MIT
|
151
|
+
metadata: {}
|
152
|
+
post_install_message:
|
153
|
+
rdoc_options: []
|
154
|
+
require_paths:
|
155
|
+
- lib
|
156
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - ">="
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
requirements: []
|
167
|
+
rubyforge_project:
|
168
|
+
rubygems_version: 2.7.8
|
169
|
+
signing_key:
|
170
|
+
specification_version: 4
|
171
|
+
summary: Classify short texts with neural network
|
172
|
+
test_files: []
|