ai-nlp 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/MIT-LICENSE +20 -0
- data/README.md +114 -0
- data/Rakefile +38 -0
- data/app/assets/config/ai_nlp_manifest.js +2 -0
- data/app/assets/javascripts/ai/nlp/application.js +13 -0
- data/app/assets/stylesheets/ai/nlp/application.css +15 -0
- data/app/controllers/ai/nlp/application_controller.rb +12 -0
- data/app/helpers/ai/nlp/application_helper.rb +11 -0
- data/app/jobs/ai/nlp/application_job.rb +11 -0
- data/app/mailers/ai/nlp/application_mailer.rb +13 -0
- data/app/models/ai/nlp/application_record.rb +12 -0
- data/app/models/ai/nlp/language.rb +23 -0
- data/app/views/layouts/ai/nlp/application.html.erb +14 -0
- data/config/routes.rb +4 -0
- data/db/migrate/20170907142959_create_ia_taln_languages.rb +12 -0
- data/lib/ai/nlp.rb +12 -0
- data/lib/ai/nlp/engine.rb +12 -0
- data/lib/ai/nlp/languages.rb +95 -0
- data/lib/ai/nlp/n_gram/hasher.rb +118 -0
- data/lib/ai/nlp/n_gram/n_gram.rb +29 -0
- data/lib/ai/nlp/stem/fr.md +178 -0
- data/lib/ai/nlp/stem/stem.rb +0 -0
- data/lib/ai/nlp/version.rb +7 -0
- data/lib/tasks/ai/nlp_tasks.rake +5 -0
- metadata +112 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c966d762ba275e10544c946f454d9dad7334edb6
|
4
|
+
data.tar.gz: 10cab1a81834a14716bb4c187b14af1ec54c27f5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a6ec561914777c59f0e8d44ed4ade7cde89ff409ba1bae3efca1816161fbb01ee82bf102ed4a4c0530eb500ba8737c07f00ae6752862b58f21586e68fa0b00b5
|
7
|
+
data.tar.gz: cf2080d53cae13af26a85d9b9bd6961f1ce703d749f2d5ede844bed9adc363bfd60b582d5f3309e514a6309c98c0b4ccb1bae05eea663780d8213681e30c4da8
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2017 Alain ANDRE
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
[![pipeline status](https://gitlab.com/al1_andre/ai-nlp/badges/master/build.svg)](https://gitlab.com/al1_andre/ai-nlp/commits/master)
|
2
|
+
[![Coverage report](https://gitlab.com/al1_andre/ai-nlp/badges/master/coverage.svg?job=rspec)](http://al1_andre.gitlab.io/ai-nlp)
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/ai-nlp.svg)](https://badge.fury.io/rb/ai-nlp)
|
4
|
+
|
5
|
+
# Objet
|
6
|
+
J'entends de plus en plus parler de l'**intelligence artificielle** et des avancées sidérantes des **GAFA** et autres **NATU** dans ce domaine et ça m’intéresse beaucoup. Je souhaite en **apprendre** plus mais si l'Internet est plein d'informations, il faut tout de même savoir la chercher. Il y a de nombreux articles en **Anglais**, peu en **Français** et les concepts ne sont pas simple de prime abord. Je souhaite donc apporter ma pierre à l'édifice en **partageant** ce que j'apprends. C'est un projet à but non lucratif, ouvert à tous sous licence MIT dont le seul but est l'**apprentissage** par l'**expérience**. Le répertoire d'exercices se trouve [ici](exercices/)
|
7
|
+
|
8
|
+
# Description
|
9
|
+
> On regroupe habituellement sous le terme d'intelligence artificielle un ensemble de notions s'inspirant de la cognition humaine ou du cerveau biologique, et destiné s à assister ou suppléer l'individu dans le traitement des informations massives.
|
10
|
+
|
11
|
+
En janvier **2017**, le **secrétaire d’État chargé de l'Industrie, du Numérique et de l'Innovation** et le **secrétaire d’État chargé de l'Enseignement supérieur et de la Recherche** ont lancé la démarche **#franceIA** qui avait pour objectif d'étudier les opportunités de l'**IA**. Cette étude se structure autour de **trois piliers** qui ont été déclinés en **dix thèmes** clés et traités par dix **groupes de travail** et sept sous-groupes. Leur rapport de synthèse est disponible [ici](https://www.economie.gouv.fr/files/files/PDF/2017/Rapport_synthese_France_IA_.pdf).
|
12
|
+
|
13
|
+
L'intelligence artificielle traite les sujets suivants :
|
14
|
+
- La résolution des problèmes
|
15
|
+
- La [représentation des connaissances](https://fr.wikipedia.org/wiki/Repr%C3%A9sentation_des_connaissances)
|
16
|
+
- L'[organisation](https://fr.wikipedia.org/wiki/Planification_(intelligence_artificielle))
|
17
|
+
- L'[apprentissage](https://fr.wikipedia.org/wiki/Apprentissage_automatique)
|
18
|
+
- La [compréhension du langage humain (NLP/TALN)](https://fr.wikipedia.org/wiki/Traitement_automatique_du_langage_naturel)
|
19
|
+
- La[perception machine](https://fr.wikipedia.org/wiki/Machine_perception)
|
20
|
+
- La [robotique](https://fr.wikipedia.org/wiki/Robotique)
|
21
|
+
- L'[information affective](https://fr.wikipedia.org/wiki/Informatique_affective)
|
22
|
+
- La [créativité](https://en.wikipedia.org/wiki/Computational_creativity)
|
23
|
+
- La [conscience de soi](https://fr.wikipedia.org/wiki/Intelligence_artificielle#Intelligence_artificielle_forte)
|
24
|
+
|
25
|
+
## Pourquoi un tutoriel en français ?
|
26
|
+
Il ne faut pas se **mentir**, la langue **universelle** de la **technique** est bel et bien l'**anglais** aujourd'hui. Mon code est en anglais ainsi que ses commentaires. C'est une **obligation** si l'on veut pouvoir **partager** avec le plus grand monde possible ; étant un grand défenseur de l'**open-source**, le partage du code est ce qui le rend plus fort, plus **sûr**.
|
27
|
+
|
28
|
+
Si les **Français** se trouvent assez **bien représentés** en ce qui touche à l'**I**ntelligence **A**rtificielle comme le dit le rapport du gouvernement, les **articles** en Français sur Internet sont vieux, vide de sens des fois, trop **compliqués** souvent ; mais surtout **jamais sexy** ! L'IA semble, pour le commun des mortels, **intouchable**, **stratosphérique** et même considéré à surtout éviter ! Hors il y a un **enjeu** national de taille. Nous avons **raté** le passage à l'**Internet** parce que nous pensions être les **maitres du monde** avec notre **minitel** ; il s'agit là de ne pas loupé la marche !
|
29
|
+
|
30
|
+
Si les **infrastructures** françaises sont à la ramasse (quasi **inaccessibles**), nos **ingénieurs** se **vendent** bien ; mais pas chez nous ! Si nous étudions l'IA, en parlons ; si nous écrivons des articles dessus, c'est en **anglais** ! Hors dans les enjeux de l'IA, on compte l'**économie**, la **santé**, l'**industrie** qui ont des dimensions **humaines** fortes ; si on **alimente** les IA avec seulement des **données** et des **concepts** en **anglais**, on peut oublier le mode de **pensée à la française** qui pourtant historiquement a une bonne réputation.
|
31
|
+
|
32
|
+
Et l'IA va rapidement **devenir** un outil d'**aide** à la prise de décision dans les **entreprises** et dans la **société**. C'est **déjà** le cas pour vos **recherches** sur Internet, vos **itinéraires** en voitures, la gestion de **vos données** sur les **clouds** etc. Lorsque **vous** répondez à un captcha afin de savoir si vous êtes bien un **humain**, vous pensez vraiment que **Google** ne vous **utilise** pas pour **former** une IA ? Si **Skynet** vous dit quelque chose, je vous laisse comprendre l'**importance** d'une **indépendance** de l'IA ou tout du moins d'une IA **alliée** (française ou européenne).
|
33
|
+
|
34
|
+
# Mon idée
|
35
|
+
Comme le soulève le **rapport de synthèse de France IA** de 2017, la France **manque** cruellement de **tutoriels** et de cours abordables en ce qui concerne l'IA ; mon **idée** est d'en **créer** un qui soit **reproduisible**, **accessible**, **ludique** et **agréable**.
|
36
|
+
|
37
|
+
Si j'ai appris une chose au fil de mes années d’**expériences** de développeur, c'est qu'il ne faut pas **réinventer** la roue ! Si on a une idée, quelqu'un dans le monde l'a très probablement **déjà eu**, et il vaut mieux **réutiliser** son travail. La **difficulté** est de le trouver et d'en avoir les **droits**. Par exemple, **Facebook** met à disposition des outils pour jouer avec son IA [wit](https://wit.ai/) qui permet, entre autre, de créer des chat-bot mais l'**IA** et ses **données** se trouve **chez eux**. Autrement dit, pendant que **vous entrainez** votre bot, vous entrainez surtout **le leur**. Il n'y a à ma connaissance que l'[OpenIA](https://openai.com/) d'[Elon Musk](https://fr.wikipedia.org/wiki/Elon_Musk) qui se veut universelle et pour l'**humanité** plus que pour l'entreprise et le **profit**. Si le profit est bon, il ne doit être qu'un **résultat** et non un **objectif** !
|
38
|
+
|
39
|
+
Je pense donc me baser sur des **concepts**, rechercher du code les gérants plus ou moins et tenter de recréer une IA **disponible** pour **tous** (disponible sous la gem [ai-nlp](https://rubygems.org/gems/ai-nlp)).
|
40
|
+
|
41
|
+
Je dois faire un choix sur le type d'IA que je souhaite créer. Je suis un **littéraire**, et la plupart des cours et informations que je trouve sur l'IA sont basés sur des **mathématiques** ; alors j'ai décidé d'accer mon IA sur les éléments suivants :
|
42
|
+
|
43
|
+
- L'[apprentissage](https://fr.wikipedia.org/wiki/Apprentissage_automatique)
|
44
|
+
- La [compréhension du langage humain (NLP/TALN)](https://fr.wikipedia.org/wiki/Traitement_automatique_du_langage_naturel)
|
45
|
+
|
46
|
+
Cette IA doit donc **apprendre** à **comprendre** un **texte humain**. Une interface web doit **afficher** de manière **graphique** la somme de **connaissances** qu'elle acquière, contenir une zone permettant à l'humain de **communiquer** avec l'IA et une zone dans laquelle elle **répond**.
|
47
|
+
|
48
|
+
# Technologies et concepts
|
49
|
+
Au tout début, mon IA ne **comprendra** rien à rien ! Il va falloir mettre en place un système d'**apprentissage** !
|
50
|
+
|
51
|
+
Mais qu'est-ce que l'apprentissage ? Il s'agit d'**analyser**, du **regrouper**, de **stocker** l'information afin de pouvoir l'**exploiter**.
|
52
|
+
|
53
|
+
## L'Analyse
|
54
|
+
> L’analyse sémantique représente l’ensemble des procédés visant à analyser le sens des mots et des phrases, elle est le plus souvent utilisée comme préambule au traitement automatique des langues.
|
55
|
+
|
56
|
+
Si je veux que l'IA comprenne le langage humain, je dois tout d'abord trouver comment réaliser les actions suivantes :
|
57
|
+
- Déterminer la langue (cf [N-gramme](https://fr.wikipedia.org/wiki/N-gramme))
|
58
|
+
- Déterminer la [syntaxe](https://fr.wikipedia.org/wiki/Analyse_syntaxique)
|
59
|
+
- Découper les éléments de la phrase [entités lexicales](https://fr.wikipedia.org/wiki/Analyse_lexicale) (ou tokens en anglais)
|
60
|
+
|
61
|
+
### Le langage informatique
|
62
|
+
Je suis un grand fan de [Ruby](https://www.ruby-lang.org/fr/), c'est un langage dont la philosophie est basée sur le [paradigme objet](https://fr.wikipedia.org/wiki/Smalltalk) ; il est donc réflexif et dynamiquement typé. je pense qu'il est capable d'apporter des avantages à mon projet grâce à sa [réflexivité](https://fr.wikipedia.org/wiki/R%C3%A9flexion_(informatique)) ; c'est à dire qu'un programme Ruby est capable d'examiner, et éventuellement modifier, ses propres structures internes de haut niveau lors de son exécution !
|
63
|
+
|
64
|
+
Il y a, à mon avis, une belle opportunité à **exploiter** pour tout ce qui concerne les [lemmes](https://fr.wikipedia.org/wiki/Lemme_(linguistique)).
|
65
|
+
|
66
|
+
## Le Regroupement
|
67
|
+
> Le clustering : Il s'agit, pour un logiciel, de diviser un groupe hétérogène de données, en sous-groupes de manière que les données considérées comme les plus similaires soient associées au sein d'un groupe homogène et qu'au contraire les données considérées comme différentes se retrouvent dans d'autres groupes distincts ; l'objectif étant de permettre une extraction de connaissance organisée à partir de ces données.
|
68
|
+
|
69
|
+
Une fois mes éléments de phrase correctement **identifiés**, je dois réussir à les **regrouper**. Il est possible de les regrouper par sens (homonymes, synonymes, racines, préfixes, suffixes, lemmes) par type (nom, verbe, adjectif, etc.)
|
70
|
+
|
71
|
+
Ce regroupement est donc **crucial**, c'est lui qui va me permettre de **stocker** au bon **endroit** mes données. Je dois être capable de faire ces regroupements de façon **non supervisés** dans certains cas et **supervisé** dans d'autres.
|
72
|
+
|
73
|
+
### Non supervisés
|
74
|
+
Je peux donc automatiquement regrouper, classifier des éléments que j'ai découpé à l'aide de ma base de données. Mais si par exemple aucune donnée ne me permet de faire un regroupement non supervisé, je dois interagir avec l'utilisateur ; je dois lui demander de m'aider à classer cet élément.
|
75
|
+
|
76
|
+
### Supervisés
|
77
|
+
L'IA doit être en mesure d’interagir avec l'utilisateur et de lui demander de l'aide pour classifier les éléments qu'il aura découpé.
|
78
|
+
|
79
|
+
L'utilisateur doit permettre de préciser :
|
80
|
+
- le type
|
81
|
+
- le sens
|
82
|
+
- l'origine (lemme)
|
83
|
+
|
84
|
+
## Le stockage
|
85
|
+
Le stockage doit permettre une récupération des données qui permette une bonne exploitation de ces dernières.
|
86
|
+
|
87
|
+
## L'exploitation
|
88
|
+
Tout ceci est magnifique, mais si l'IA arrive maintenant à **découper** et faire des **regroupements** d'information, il lui faut pourvoir les **exploiter**. L'avantage d'un **moteur de recherche** comme **QWANT** ou **Google**, par exemple, c'est qu'il sait que sa seule **action** est de **fournir des données** qu'il a collecté et regroupé à la **demande** ; il n'a pas vraiment besoin de **comprendre** ce qu'il stocke.
|
89
|
+
|
90
|
+
Une IA plus évoluée comme **SIRI** par contre, est censée **répondre** à diverses **actions** comme **ajouter** un évènements à l'agenda, **donner** le temps qu'il fera **demain** ou encore **trouver** le **chemin** d'un magasin **proche** de notre position. Il faut donc qu'il **comprenne** le **type de demande** qui est faite.
|
91
|
+
|
92
|
+
# Références
|
93
|
+
- [Rapport France-IA](https://www.economie.gouv.fr/France-IA-intelligence-artificielle#L%27IA)
|
94
|
+
|
95
|
+
## Deep Learning
|
96
|
+
- [wiki](https://fr.wikipedia.org/wiki/Apprentissage_profond)
|
97
|
+
- [Le monde](http://www.lemonde.fr/pixels/article/2015/07/24/comment-le-deep-learning-revolutionne-l-intelligence-artificielle_4695929_4408996.html)
|
98
|
+
- [ruby introduction](https://speakerdeck.com/geoffreylitt/deep-learning-an-introduction-for-ruby-developers)
|
99
|
+
|
100
|
+
## NLP
|
101
|
+
- [TAL](http://blog.onyme.com/traitement-automatique-des-langues-tal-intelligence-artificielle-ia-analyse-semantique-et-clusterings/)
|
102
|
+
- [Lemmatisation](http://blog.onyme.com/lemmatisation-et-racinisation-en-francais-flexion-lemme-et-racine-dun-mot/)
|
103
|
+
- [wiki](https://en.wikipedia.org/wiki/Natural_language_processing)
|
104
|
+
- [Structure de la phrase en Français](http://la-conjugaison.nouvelobs.com/regles/grammaire/les-structures-de-phrases-167.php)
|
105
|
+
- [lemmatizer](https://github.com/yohasebe/lemmatizer)
|
106
|
+
|
107
|
+
## Programmes/API
|
108
|
+
- [Facebook wit](https://wit.ai/)
|
109
|
+
- [Deeptext](https://humanoides.fr/deeptext-la-nouvelle-intelligence-artificielle-de-facebook-pour-decortiquer-la-semantique/)
|
110
|
+
|
111
|
+
## Ruby
|
112
|
+
- [AI4R](https://github.com/SergioFierens/ai4r/)
|
113
|
+
- [rubynlp](http://rubynlp.org/)
|
114
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
require "bundler/setup"
|
5
|
+
rescue LoadError
|
6
|
+
puts "You must `gem install bundler` and `bundle install` to run rake tasks"
|
7
|
+
end
|
8
|
+
|
9
|
+
require "rdoc/task"
|
10
|
+
|
11
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
12
|
+
rdoc.rdoc_dir = "rdoc"
|
13
|
+
rdoc.title = "Ai::Nlp"
|
14
|
+
rdoc.options << "--line-numbers"
|
15
|
+
rdoc.rdoc_files.include("README.md")
|
16
|
+
rdoc.rdoc_files.include("lib/**/*.rb")
|
17
|
+
end
|
18
|
+
|
19
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
20
|
+
load "rails/tasks/engine.rake"
|
21
|
+
|
22
|
+
|
23
|
+
load "rails/tasks/statistics.rake"
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
require "bundler/gem_tasks"
|
28
|
+
|
29
|
+
require "rake/testtask"
|
30
|
+
|
31
|
+
Rake::TestTask.new(:test) do |t|
|
32
|
+
t.libs << "test"
|
33
|
+
t.pattern = "test/**/*_test.rb"
|
34
|
+
t.verbose = false
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
task default: :test
|
@@ -0,0 +1,13 @@
|
|
1
|
+
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
2
|
+
// listed below.
|
3
|
+
//
|
4
|
+
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
5
|
+
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
6
|
+
//
|
7
|
+
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
8
|
+
// compiled file. JavaScript code in this file should be added after the last require_* statement.
|
9
|
+
//
|
10
|
+
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
11
|
+
// about supported directives.
|
12
|
+
//
|
13
|
+
//= require_tree .
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Module containing artificial intelligence tools
|
4
|
+
module Ai
|
5
|
+
# Module containing automatic natural language processing tools
|
6
|
+
module Nlp
|
7
|
+
# Main controller
|
8
|
+
class ApplicationController < ActionController::Base
|
9
|
+
protect_from_forgery with: :exception
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Module containing artificial intelligence tools
|
4
|
+
module Ai
|
5
|
+
# Module containing automatic natural language processing tools
|
6
|
+
module Nlp
|
7
|
+
# Main mailer
|
8
|
+
class ApplicationMailer < ActionMailer::Base
|
9
|
+
default from: "from@example.com"
|
10
|
+
layout "mailer"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Module containing artificial intelligence tools
|
4
|
+
module Ai
|
5
|
+
# Module containing automatic natural language processing tools
|
6
|
+
module Nlp
|
7
|
+
# Classe mère
|
8
|
+
class ApplicationRecord < ActiveRecord::Base
|
9
|
+
self.abstract_class = true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Module containing artificial intelligence tools
|
4
|
+
module Ai
|
5
|
+
# Module containing automatic natural language processing tools
|
6
|
+
module Nlp
|
7
|
+
# Manages a language
|
8
|
+
class Language < ApplicationRecord
|
9
|
+
validates_presence_of :name
|
10
|
+
serialize :map, Hash
|
11
|
+
|
12
|
+
##
|
13
|
+
# Add n-grams to the language
|
14
|
+
# @param array given_array The array of [gram, frequancy]
|
15
|
+
def add_grams(given_array)
|
16
|
+
given_array.to_h.each do |key, freq|
|
17
|
+
map[key] = map[key] ? letters + freq : freq
|
18
|
+
end
|
19
|
+
update
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Ia taln</title>
|
5
|
+
<%= stylesheet_link_tag "ai/nlp/application", media: "all" %>
|
6
|
+
<%= javascript_include_tag "ai/nlp/application" %>
|
7
|
+
<%= csrf_meta_tags %>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
|
11
|
+
<%= yield %>
|
12
|
+
|
13
|
+
</body>
|
14
|
+
</html>
|
data/config/routes.rb
ADDED
data/lib/ai/nlp.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "ai/nlp/engine"
|
4
|
+
require "ai/nlp/n_gram/n_gram"
|
5
|
+
require "ai/nlp/languages"
|
6
|
+
|
7
|
+
# Module containing artificial intelligence tools
|
8
|
+
module Ai
|
9
|
+
# Module containing automatic natural language processing tools
|
10
|
+
module Nlp
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Module containing artificial intelligence tools
|
4
|
+
module Ai
|
5
|
+
# Module containing automatic natural language processing tools
|
6
|
+
module Nlp
|
7
|
+
# Engine and isolation namespace
|
8
|
+
class Engine < ::Rails::Engine
|
9
|
+
isolate_namespace Ai::Nlp
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "ai/nlp/n_gram/n_gram"
|
5
|
+
|
6
|
+
# Module containing artificial intelligence tools
|
7
|
+
module Ai
|
8
|
+
# Module containing automatic natural language processing tools
|
9
|
+
module Nlp
|
10
|
+
# Class to handle multiple languages
|
11
|
+
class Languages
|
12
|
+
##
|
13
|
+
# Initialisation
|
14
|
+
def initialize
|
15
|
+
@n_gram = NGram.new
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Returns the currently known languages
|
20
|
+
# @return An array of Language
|
21
|
+
def all
|
22
|
+
@languages = Language.all
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# Offers among the available languages the closest one to the datasets
|
27
|
+
# @param string input The data set.
|
28
|
+
def guess(input)
|
29
|
+
all
|
30
|
+
return [] if @languages.empty?
|
31
|
+
hash = @languages.map { |language| [language, score(input, language)] }.to_h
|
32
|
+
sort(hash)
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Create a new language.
|
37
|
+
# @param string name The language name.
|
38
|
+
# @param string input The initial data set.
|
39
|
+
# @return La langue créée.
|
40
|
+
def create_one(name, input)
|
41
|
+
language = Language.new(name: name)
|
42
|
+
language.update(map: @n_gram.calculate(input).to_h)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
##
|
48
|
+
# Sort the language hash
|
49
|
+
# @param hash hash The language hash
|
50
|
+
# @return the sorted list of languages
|
51
|
+
def sort(hash)
|
52
|
+
sorted_languages = @languages.sort_by { |language| hash[language] }
|
53
|
+
reject(sorted_languages, hash)
|
54
|
+
end
|
55
|
+
|
56
|
+
def reject(sorted_languages, hash)
|
57
|
+
sorted_languages.reject { |language| hash[language].zero? }
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Compare a string of characters against a language based on, at most,
|
62
|
+
# the 400 most commonly used groups of letters.
|
63
|
+
# @param string input The data set to compare
|
64
|
+
# @param Language language The Language to compare to
|
65
|
+
def score(input, language)
|
66
|
+
input_gram = @n_gram.calculate(input)
|
67
|
+
ngram = language.map
|
68
|
+
calculate_point([input_gram.size, 400].min, ngram, input_gram)
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Calculates the new frequency
|
73
|
+
# @return le score (point)
|
74
|
+
def calculate_point(max_compare, ngram, input_gram)
|
75
|
+
point = 0
|
76
|
+
(0..max_compare).each do |pos|
|
77
|
+
position = input_gram[pos]
|
78
|
+
next unless position
|
79
|
+
point = add_frequency(ngram[position[0]], pos, point)
|
80
|
+
end
|
81
|
+
point
|
82
|
+
end
|
83
|
+
|
84
|
+
##
|
85
|
+
# Add frequency if needed
|
86
|
+
# @param integer input_gram_freq The input gram frequency
|
87
|
+
# @param integer pos The position in the max_compare
|
88
|
+
# @param integer point The current calculated points
|
89
|
+
def add_frequency(input_gram_freq, pos, point)
|
90
|
+
point += (input_gram_freq - pos).abs if input_gram_freq
|
91
|
+
point
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "sanitize"
|
5
|
+
require "cgi"
|
6
|
+
require "unicode"
|
7
|
+
require "byebug"
|
8
|
+
|
9
|
+
# Module containing artificial intelligence tools
|
10
|
+
module Ai
|
11
|
+
# Module containing automatic natural language processing tools
|
12
|
+
module Nlp
|
13
|
+
# Class managing an n-gram hash
|
14
|
+
class Hasher
|
15
|
+
##
|
16
|
+
# Initialisation
|
17
|
+
# @param string input The string to treat
|
18
|
+
def initialize(input)
|
19
|
+
@input = input
|
20
|
+
@hash = {}
|
21
|
+
clean
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Calculates n-gram frequencies for the dataset
|
26
|
+
# @return Frequencies of ngram or sorted array
|
27
|
+
def calculate
|
28
|
+
@input.split(/[\d\s\[\]]/).each do |word|
|
29
|
+
calculate_word_gram("_#{word}_")
|
30
|
+
end
|
31
|
+
drop_unwanted_keys
|
32
|
+
@hash.sort { |one, other| other[1] <=> one[1] }
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
##
|
38
|
+
# Enriched hash representing the n-gram of a word
|
39
|
+
# @param string word The word to calculate
|
40
|
+
def calculate_word_gram(word)
|
41
|
+
length = word.size
|
42
|
+
(0..length).each do |letter_position|
|
43
|
+
parameters = { letter_position: letter_position, word: word, length: length }
|
44
|
+
calculate_letter_gram(parameters)
|
45
|
+
length -= 1
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Deletes a key if its value is less than or equal to zero
|
51
|
+
def drop_unwanted_keys
|
52
|
+
@hash.each_key do |key|
|
53
|
+
@hash.delete(key) if key.size <= 0
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Stores the mono-gram, bi-gram and tri-gram in the hash
|
59
|
+
# @param hash parameters The list of necessary parameters :
|
60
|
+
# - letter_position The position of the letter to be processed
|
61
|
+
# - word The word treated
|
62
|
+
# - length Current word size
|
63
|
+
def calculate_letter_gram(parameters)
|
64
|
+
(1..3).each do |nth|
|
65
|
+
letters = parameters[:word][parameters[:letter_position], nth]
|
66
|
+
next unless letters
|
67
|
+
init_key(letters)
|
68
|
+
@hash[letters] += 1 if parameters[:length] > (nth - 1)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# Initialize key if necessary
|
74
|
+
# @param string letters The group of letters
|
75
|
+
def init_key(letters)
|
76
|
+
@hash[letters] ||= 0
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# Cleans the string passed as argument
|
81
|
+
def clean
|
82
|
+
safe_clean
|
83
|
+
specific_clean
|
84
|
+
clean_latin
|
85
|
+
@input = @input.strip.split(" ").join(" ")
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Cleans the string from Latin characters if more than half of the string is not Latin.
|
90
|
+
def clean_latin
|
91
|
+
latin = @input.scan(/[a-z]/)
|
92
|
+
nonlatin = @input.scan(/[\p{L}&&[^a-z]]/)
|
93
|
+
nonlatin_ratio = nonlatin.size / (latin.size * 1.0)
|
94
|
+
return if nonlatin_ratio < 0.5
|
95
|
+
@input.gsub!(/[a-zA-Z]/, "") if !latin.empty? && !nonlatin.empty?
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Removes polluting web addresses, mails and characters
|
100
|
+
def specific_clean
|
101
|
+
uri_regex = %r/(?:http|https):\/\/[a-z0-9]+(?:[\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(?:(?::[0-9]{1,5})?\/[^\s]*)?/
|
102
|
+
@input.gsub!(uri_regex, "")
|
103
|
+
# Remove mails
|
104
|
+
@input.gsub!(/[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}/, "")
|
105
|
+
# Repleace polluting non-alphabetical characters, punctuation included by a space
|
106
|
+
@input.gsub!(%r/[\*\^><!\"#\$%&\'\(\)\*\+:;,\._\/=\?@\{\}\[\]|\-\n\r0-9]/, " ")
|
107
|
+
end
|
108
|
+
|
109
|
+
##
|
110
|
+
# Cleaning via existing tools
|
111
|
+
def safe_clean
|
112
|
+
@input = Sanitize.clean(@input)
|
113
|
+
@input = CGI.unescapeHTML(@input)
|
114
|
+
@input = Unicode.downcase(@input)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "ai/nlp/n_gram/hasher"
|
5
|
+
|
6
|
+
# Module containing artificial intelligence tools
|
7
|
+
module Ai
|
8
|
+
# Module containing automatic natural language processing tools
|
9
|
+
module Nlp
|
10
|
+
# Class for calculating n-grams, storing and exploiting them
|
11
|
+
class NGram
|
12
|
+
##
|
13
|
+
# Cuts the data set into a grouping of letters
|
14
|
+
# @param string input The dataset
|
15
|
+
def hash(input)
|
16
|
+
calculate(input).map { |letters, _gram| letters }
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# Calculates the n-gram frequencies for the data set passed as an argument
|
21
|
+
# @param string input The dataset
|
22
|
+
# @return Frequencies of ngram or sorted array
|
23
|
+
def calculate(input)
|
24
|
+
hash = Hasher.new(input)
|
25
|
+
hash.calculate
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
The stemming algorithm
|
2
|
+
|
3
|
+
In French the verb endings **ent** and **ons** cannot be removed without unacceptable overstemming.
|
4
|
+
|
5
|
+
The **ons** form is rarer, but **ent** forms are quite common, and will appear regularly throughout a stemmed vocabulary.
|
6
|
+
|
7
|
+
Letters in French include the following accented forms,
|
8
|
+
|
9
|
+
â à ç ë é ê è ï î ô û ù
|
10
|
+
|
11
|
+
The following letters are vowels:
|
12
|
+
|
13
|
+
a e i o u y â à ë é ê è ï î ô û ù
|
14
|
+
|
15
|
+
Assume the word is in lower case. Then put into upper case u or i preceded and followed by a vowel, and y preceded or followed by a vowel. u after q is also put into upper case. For example,
|
16
|
+
|
17
|
+
jouer -> joUer
|
18
|
+
ennuie -> ennuIe
|
19
|
+
yeux -> Yeux
|
20
|
+
quand -> qUand
|
21
|
+
|
22
|
+
(The upper case forms are not then classed as vowels — see note on vowel marking.)
|
23
|
+
|
24
|
+
If the word begins with two vowels, RV is the region after the third letter, otherwise the region after the first vowel not at the beginning of the word, or the end of the word if these positions cannot be found. (Exceptionally, par, col or tap, at the begining of a word is also taken to define RV as the region to their right.)
|
25
|
+
|
26
|
+
For example,
|
27
|
+
|
28
|
+
a i m e r a d o r e r v o l e r t a p i s
|
29
|
+
|...| |.....| |.....| |...|
|
30
|
+
|
31
|
+
R1 is the region after the first non-vowel following a vowel, or the end of the word if there is no such non-vowel. R2 is the region after the first non-vowel following a vowel in R1, or the end of the word if there is no such non-vowel. (See note on R1 and R2.)
|
32
|
+
|
33
|
+
For example:
|
34
|
+
|
35
|
+
f a m e u s e m e n t
|
36
|
+
|......R1.......|
|
37
|
+
|...R2....|
|
38
|
+
|
39
|
+
Note that R1 can contain RV (adorer), and RV can contain R1 (voler).
|
40
|
+
|
41
|
+
Below, ‘delete if in R2’ means that a found suffix should be removed if it lies entirely in R2, but not if it overlaps R2 and the rest of the word. ‘delete if in R1 and preceded by X’ means that X itself does not have to come in R1, while ‘delete if preceded by X in R1’ means that X, like the suffix, must be entirely in R1.
|
42
|
+
|
43
|
+
Start with step 1
|
44
|
+
|
45
|
+
Step 1: Standard suffix removal
|
46
|
+
|
47
|
+
Search for the longest among the following suffixes, and perform the action indicated.
|
48
|
+
|
49
|
+
ance iqUe isme able iste eux ances iqUes ismes ables istes
|
50
|
+
delete if in R2
|
51
|
+
|
52
|
+
atrice ateur ation atrices ateurs ations
|
53
|
+
delete if in R2
|
54
|
+
if preceded by ic, delete if in R2, else replace by iqU
|
55
|
+
|
56
|
+
logie logies
|
57
|
+
replace with log if in R2
|
58
|
+
|
59
|
+
usion ution usions utions
|
60
|
+
replace with u if in R2
|
61
|
+
|
62
|
+
ence ences
|
63
|
+
replace with ent if in R2
|
64
|
+
|
65
|
+
ement ements
|
66
|
+
delete if in RV
|
67
|
+
if preceded by iv, delete if in R2 (and if further preceded by at, delete if in R2), otherwise,
|
68
|
+
if preceded by eus, delete if in R2, else replace by eux if in R1, otherwise,
|
69
|
+
if preceded by abl or iqU, delete if in R2, otherwise,
|
70
|
+
if preceded by ièr or Ièr, replace by i if in RV
|
71
|
+
|
72
|
+
ité ités
|
73
|
+
delete if in R2
|
74
|
+
if preceded by abil, delete if in R2, else replace by abl, otherwise,
|
75
|
+
if preceded by ic, delete if in R2, else replace by iqU, otherwise,
|
76
|
+
if preceded by iv, delete if in R2
|
77
|
+
|
78
|
+
if ive ifs ives
|
79
|
+
delete if in R2
|
80
|
+
if preceded by at, delete if in R2 (and if further preceded by ic, delete if in R2, else replace by iqU)
|
81
|
+
|
82
|
+
eaux
|
83
|
+
replace with eau
|
84
|
+
|
85
|
+
aux
|
86
|
+
replace with al if in R1
|
87
|
+
|
88
|
+
euse euses
|
89
|
+
delete if in R2, else replace by eux if in R1
|
90
|
+
|
91
|
+
issement issements
|
92
|
+
delete if in R1 and preceded by a non-vowel
|
93
|
+
|
94
|
+
amment
|
95
|
+
replace with ant if in RV
|
96
|
+
|
97
|
+
emment
|
98
|
+
replace with ent if in RV
|
99
|
+
|
100
|
+
ment ments
|
101
|
+
delete if preceded by a vowel in RV
|
102
|
+
|
103
|
+
In steps 2a and 2b all tests are confined to the RV region.
|
104
|
+
|
105
|
+
Do step 2a if either no ending was removed by step 1, or if one of endings amment, emment, ment, ments was found.
|
106
|
+
|
107
|
+
Step 2a: Verb suffixes beginning i
|
108
|
+
|
109
|
+
Search for the longest among the following suffixes and if found, delete if preceded by a non-vowel.
|
110
|
+
|
111
|
+
îmes ît îtes i ie ies ir ira irai iraIent irais irait iras irent irez iriez irions irons iront is issaIent issais issait issant issante issantes issants isse issent isses issez issiez issions issons it
|
112
|
+
|
113
|
+
|
114
|
+
(Note that the non-vowel itself must also be in RV.)
|
115
|
+
|
116
|
+
Do step 2b if step 2a was done, but failed to remove a suffix.
|
117
|
+
|
118
|
+
Step 2b: Other verb suffixes
|
119
|
+
|
120
|
+
Search for the longest among the following suffixes, and perform the action indicated.
|
121
|
+
|
122
|
+
ions
|
123
|
+
delete if in R2
|
124
|
+
|
125
|
+
é ée ées és èrent er era erai eraIent erais erait eras erez eriez erions erons eront ez iez
|
126
|
+
delete
|
127
|
+
|
128
|
+
âmes ât âtes a ai aIent ais ait ant ante antes ants as asse assent asses assiez assions
|
129
|
+
delete
|
130
|
+
if preceded by e, delete
|
131
|
+
|
132
|
+
|
133
|
+
(Note that the e that may be deleted in this last step must also be in RV.)
|
134
|
+
|
135
|
+
If the last step to be obeyed — either step 1, 2a or 2b — altered the word, do step 3
|
136
|
+
|
137
|
+
Step 3
|
138
|
+
|
139
|
+
Replace final Y with i or final ç with c
|
140
|
+
|
141
|
+
Alternatively, if the last step to be obeyed did not alter the word, do step 4
|
142
|
+
|
143
|
+
Step 4: Residual suffix
|
144
|
+
|
145
|
+
If the word ends s, not preceded by a, i, o, u, è or s, delete it.
|
146
|
+
|
147
|
+
In the rest of step 4, all tests are confined to the RV region.
|
148
|
+
|
149
|
+
Search for the longest among the following suffixes, and perform the action indicated.
|
150
|
+
|
151
|
+
ion
|
152
|
+
delete if in R2 and preceded by s or t
|
153
|
+
|
154
|
+
ier ière Ier Ière
|
155
|
+
replace with i
|
156
|
+
|
157
|
+
e
|
158
|
+
delete
|
159
|
+
|
160
|
+
ë
|
161
|
+
if preceded by gu, delete
|
162
|
+
|
163
|
+
|
164
|
+
(So note that ion is removed only when it is in R2 — as well as being in RV — and preceded by s or t which must be in RV.)
|
165
|
+
|
166
|
+
Always do steps 5 and 6.
|
167
|
+
|
168
|
+
Step 5: Undouble
|
169
|
+
|
170
|
+
If the word ends enn, onn, ett, ell or eill, delete the last letter
|
171
|
+
|
172
|
+
Step 6: Un-accent
|
173
|
+
|
174
|
+
If the words ends é or è followed by at least one non-vowel, remove the accent from the e.
|
175
|
+
|
176
|
+
And finally:
|
177
|
+
|
178
|
+
Turn any remaining I, U and Y letters in the word back into lower case.
|
File without changes
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ai-nlp
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alain ANDRE
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-09-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.1.3
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.1.3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sanitize
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.5'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: unicode
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.4.4.4
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.4.4.4
|
55
|
+
description: |2
|
56
|
+
This gem contains a grouping of ruby tools related to Artificial Intelligence
|
57
|
+
and Automatic Natural Language Processing
|
58
|
+
email:
|
59
|
+
- dev@alain-andre.fr
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- MIT-LICENSE
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- app/assets/config/ai_nlp_manifest.js
|
68
|
+
- app/assets/javascripts/ai/nlp/application.js
|
69
|
+
- app/assets/stylesheets/ai/nlp/application.css
|
70
|
+
- app/controllers/ai/nlp/application_controller.rb
|
71
|
+
- app/helpers/ai/nlp/application_helper.rb
|
72
|
+
- app/jobs/ai/nlp/application_job.rb
|
73
|
+
- app/mailers/ai/nlp/application_mailer.rb
|
74
|
+
- app/models/ai/nlp/application_record.rb
|
75
|
+
- app/models/ai/nlp/language.rb
|
76
|
+
- app/views/layouts/ai/nlp/application.html.erb
|
77
|
+
- config/routes.rb
|
78
|
+
- db/migrate/20170907142959_create_ia_taln_languages.rb
|
79
|
+
- lib/ai/nlp.rb
|
80
|
+
- lib/ai/nlp/engine.rb
|
81
|
+
- lib/ai/nlp/languages.rb
|
82
|
+
- lib/ai/nlp/n_gram/hasher.rb
|
83
|
+
- lib/ai/nlp/n_gram/n_gram.rb
|
84
|
+
- lib/ai/nlp/stem/fr.md
|
85
|
+
- lib/ai/nlp/stem/stem.rb
|
86
|
+
- lib/ai/nlp/version.rb
|
87
|
+
- lib/tasks/ai/nlp_tasks.rake
|
88
|
+
homepage: https://gitlab.com/al1_andre/ai-nlp
|
89
|
+
licenses:
|
90
|
+
- MIT
|
91
|
+
metadata: {}
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '2.3'
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 2.5.1
|
109
|
+
signing_key:
|
110
|
+
specification_version: 4
|
111
|
+
summary: Artificial Intelligence and Automatic Natural Language Processing
|
112
|
+
test_files: []
|