Jouons avec les données Amedas - Partie 4

La dernière fois, j'ai écrit comment utiliser le modèle d'analyse de régression dans Keras. Cette fois, j'écrirai un peu sur le modèle de classification.

Tout d'abord, il existe diverses informations sur la façon de créer un modèle de classification dans Keras, mais je me suis référé au blog suivant.

Exemple d'apprentissage en profondeur extrêmement simple par Keras

Je me réfère également au site officiel de Keras.

Les informations utilisées sont les mêmes que la dernière fois, les données d'un jour d'Amedas. Il est à nouveau montré ci-dessous. (J'ai l'impression que la façon de le faire était écrite autour de celle-là)

data_out.csv


year,month,day,hour,temp,wind,angle,weather,
2019,8,13,1,24.9,1.4,0,2
2019,8,13,2,24.1,2.2,0,2
2019,8,13,3,23.8,1.4,0,2
2019,8,13,4,23.5,1.2,0,2
2019,8,13,5,23.2,1.8,0,2
2019,8,13,6,23.9,0.7,15,2
2019,8,13,7,25.1,0.9,13,2
2019,8,13,8,26.7,1.0,10,2
2019,8,13,9,28.6,1.6,5,2
2019,8,13,10,30.3,1.2,8,2
2019,8,13,11,30.6,1.3,11,2
2019,8,13,12,31.4,2.5,1,2
2019,8,13,13,33.3,2.0,5,2
2019,8,13,14,33.0,2.3,16,2
2019,8,13,15,33.9,1.8,3,2
2019,8,13,16,32.2,3.2,13,2
2019,8,13,17,29.4,1.0,15,10
2019,8,13,18,27.1,4.4,11,10
2019,8,13,19,25.9,3.7,13,10
2019,8,13,20,26.0,2.4,16,4
2019,8,13,21,26.0,0.9,16,4
2019,8,13,22,25.6,1.3,16,2
2019,8,13,23,25.4,2.6,0,2

Je me demandais quoi classer, mais si la vitesse du vent est supérieure à une certaine valeur (par exemple, ** 2m **), je la diviserai en deux modèles: vent fort, et sinon, brise / pas de vent.

La préparation des données (ingestion, normalisation, etc.) est la suivante.

import pandas as pd
import numpy as np

# deta making???
csv_input = pd.read_csv(filepath_or_buffer="data_out.csv",
                        encoding="ms932",
                        sep=",")

#Nombre d'éléments d'entrée (nombre de lignes)*Le nombre de colonnes) sera renvoyé.
#Renvoie l'objet DataFrame extrait uniquement pour la colonne spécifiée.
x = np.array(csv_input[["hour"]])
y = np.array(csv_input[["wind"]])

# num of records
N = len(x)

#Normalisation
x_max = np.max(x,axis=0)
x_min = np.min(x,axis=0)
x = (x - np.min(x,axis=0))/(np.max(x,axis=0) - np.min(x,axis=0))

# y >  2[m] : strong
# y <= 2[m] : weak
y_new = np.zeros(len(y),dtype=int)
for k in range(len(y)):
    if y[k] > 2:
        y_new[k] = 1

y = y_new.reshape(y.shape)

y contient les données de sortie

――Quand 1 → Jugé comme un vent fort --Quand 0 → Jugé comme brise / pas de vent

Il est devenu. Pour apprendre cela, créons un modèle dans Keras avec le code suivant.

#Créer un modèle d'apprentissage
model = Sequential()
#Couche entièrement connectée(1 couche->30 couches)
model.add(Dense(input_dim=1, output_dim=30, bias=True))
#Fonction d'activation(Fonction sigmoïde)
model.add(Activation("sigmoid"))

#Couche entièrement connectée(30 couches->1 couche)
model.add(Dense(output_dim=1))
#Fonction d'activation(Fonction sigmoïde)
model.add(Activation("sigmoid"))

#Compilez le modèle
model.compile(loss="binary_crossentropy", optimizer="sgd", metrics=["accuracy"])
#Effectuer l'apprentissage
model.fit(x, y, epochs=5000, batch_size=32, verbose=1)

Lorsqu'il n'y a qu'une seule partie de sortie finale, la sélection de binary_crossentropy semble être la bonne réponse. Les éléments suivants sont considérés comme les fonctions d'erreur. (Équation simplifiée 5.23 à la page 236 pour la reconnaissance de formes et l'apprentissage automatique)

E(\textbf{w}) = - \sum_{n=1}^{N}  
\{ t_{n} \ln y (x_n,\textbf{w}) + (1-t_{n})\ln (1-y (x_n,\textbf{w})) \}

L'indice n indique le nombre d'échantillons et t_n est la valeur correcte (1 ou 0) correspondant à x_n. y (x_n, w) signifie la sortie d'inférence lorsque l'entrée = x_n et le paramètre de réseau neuronal = w (à ce moment, E (w) = 0 et la valeur minimale est prise). Le processus d'apprentissage consiste à trouver w tel que t_n = y (x_n, w) soit valable pour tout x_n.

Ici, pour l'évaluation des résultats, préparez une fonction qui donne le taux de réponse correct qui a été donné auparavant. (Bien qu'il apparaisse dans le journal Keras, je l'ai préparé moi-même pour la pratique)

# y:predict
# t:true
def checkOKPercent(y,t):
    # from predict param
    sign_newral = np.sign(np.array(y).reshape([len(t),1]) - 0.5)
    # from true param
    sign_orig = np.sign(np.array(t.reshape([len(t),1])) - 0.5)
    # are there same sign??
    NGCNT = np.sum(np.abs(sign_newral-sign_orig))/2
    # calc NG percent in [0.0-1.0]
    NGPer = NGCNT / len(t)
    # return OK percent [0.0-1.0]
    return 1.0-NGPer

La sortie du réseau neuronal, y (x_n, w), prend en fait une valeur arbitraire (float ???) entre [0,1]. Si elle est égale ou supérieure à 0,5, c'est un vent fort, et si elle est inférieure à 0,5, c'est un jeu d'enfant / Cela signifie pas de vent.

Résumez ce qui précède et collez la source ci-dessous.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

from keras.models import Sequential
from keras.layers import Activation, Dense

# y:predict
# t:true
def checkOKPercent(y,t):
    # from predict param
    sign_newral = np.sign(np.array(y).reshape([len(t),1]) - 0.5)
    # from true param
    sign_orig = np.sign(np.array(t.reshape([len(t),1])) - 0.5)
    # are there same sign??
    NGCNT = np.sum(np.abs(sign_newral-sign_orig))/2
    # calc NG percent in [0.0-1.0]
    NGPer = NGCNT / len(t)
    # return OK percent [0.0-1.0]
    return 1.0-NGPer

 
# deta making???
csv_input = pd.read_csv(filepath_or_buffer="data_out.csv",
                        encoding="ms932",
                        sep=",")

#Nombre d'éléments d'entrée (nombre de lignes)*Le nombre de colonnes) sera renvoyé.
#Renvoie l'objet DataFrame extrait uniquement pour la colonne spécifiée.
x = np.array(csv_input[["hour"]])
y = np.array(csv_input[["wind"]])


# num of records
N = len(x)

#Normalisation
x_max = np.max(x,axis=0)
x_min = np.min(x,axis=0)
x = (x - np.min(x,axis=0))/(np.max(x,axis=0) - np.min(x,axis=0))


# y >  2[m] : strong
# y <= 2[m] : weak
y_new = np.zeros(len(y),dtype=int)
for k in range(len(y)):
    if y[k] > 2:
        y_new[k] = 1

y = y_new.reshape(y.shape)


#Créer un modèle d'apprentissage
model = Sequential()
#Couche entièrement connectée(1 couche->30 couches)
model.add(Dense(input_dim=1, output_dim=30, bias=True))
#Fonction d'activation(Fonction sigmoïde)
model.add(Activation("sigmoid"))

#Couche entièrement connectée(30 couches->1 couche)
model.add(Dense(output_dim=1))
#Fonction d'activation(Fonction sigmoïde)
model.add(Activation("sigmoid"))

#Compilez le modèle
model.compile(loss="binary_crossentropy", optimizer="sgd", metrics=["accuracy"])
#Effectuer l'apprentissage
model.fit(x, y, epochs=5000, batch_size=32, verbose=1)

#Tracé de la valeur réelle
plt.plot(x,y,marker='x',label="true")
#Calculer les résultats Keras avec inférence,afficher
y_predict = model.predict(x)
#Graphique des résultats du calcul Keras
plt.plot(x,y_predict,marker='x',label="predict")
#Affichage de la légende
plt.legend()
# display result
print('OK %.2f[percent]' % (checkOKPercent(y_predict,y)*100.0))

Si vous remarquez, vous pouvez supprimer tensorflow de l'importation ... Maintenant, quand je trace le résultat, il ressemble à ce qui suit.

Figure 2020-05-05 201848.png

L'axe horizontal est le temps (normalisé) et l'axe vertical indique si le vent est fort ou non. Le bleu est la bonne réponse, et l'orange est le résultat du réseau neuronal. D'une certaine manière ... Après tout, cela ressemble à une approximation en ligne droite.

Le degré de convergence est affiché sur la console, donc vérifions-le.

Epoch 4994/5000
23/23 [==============================] - 0s 87us/step - loss: 0.6013 - acc: 0.6522
Epoch 4995/5000
23/23 [==============================] - 0s 87us/step - loss: 0.6012 - acc: 0.6522
Epoch 4996/5000
23/23 [==============================] - 0s 43us/step - loss: 0.6012 - acc: 0.6522
Epoch 4997/5000
23/23 [==============================] - 0s 43us/step - loss: 0.6012 - acc: 0.6522
Epoch 4998/5000
23/23 [==============================] - 0s 43us/step - loss: 0.6012 - acc: 0.6522
Epoch 4999/5000
23/23 [==============================] - 0s 43us/step - loss: 0.6012 - acc: 0.6522
Epoch 5000/5000
23/23 [==============================] - 0s 43us/step - loss: 0.6012 - acc: 0.6522
OK 65.22[percent]

Comme la perte converge vers 0,6012, cela semble fonctionner comme une logique. Cependant, le taux de réponse correcte est de 65,22%. Même si je le prédis d'un côté ou de l'autre, je sens que ça ira comme ça, donc les performances ne sont pas bonnes.

J'ai donc essayé le modèle qui définit la valeur initiale du réseau neuronal comme je l'ai fait la dernière fois. Du coup je posterai tout le code source.

import pandas as pd
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt

from keras.models import Sequential
from keras.layers import Activation, Dense
from keras.utils.np_utils import to_categorical
from keras import backend as K
import keras

# y:predict
# t:true
def checkOKPercent(y,t):
    # from predict param
    sign_newral = np.sign(np.array(y).reshape([len(t),1]) - 0.5)
    # from true param
    sign_orig = np.sign(np.array(t.reshape([len(t),1])) - 0.5)
    # are there same sign??
    NGCNT = np.sum(np.abs(sign_newral-sign_orig))/2
    # calc NG percent in [0.0-1.0]
    NGPer = NGCNT / len(t)
    # return OK percent [0.0-1.0]
    return 1.0-NGPer


# init infomation for keras layers or models
class InitInfo:
    
    # constractor
    #  x:input y:output
    def __init__(self,x,y):
        self.x = x
        self.y = y
        
    # calc coefficient of keras models(1st layer)
    # input  s:changing point in [0,1]
    #        sign:[1]raise,[0]down
    # return b:coefficient of bias
    #        w:coefficient of x
    # notice - it can make like step function using this return values(s,sign)
    def calc_b_w(self,s,sign):
    
        N = 1000 #Stockage temporaire
        # s = -b/w
        if sign > 0:
            b = -N
        else:
            b = N
        if s != 0:
            w = -b/s
        else:
            w = 1
        return b,w
    
    # calc coefficient of keras models(1st and 2nd layer)
    def calc_w_h(self):
    
        K = len(self.x)
        # coefficient of 1st layer(x,w)
        w_array = np.zeros([K*2,2])
        # coefficient of 2nd layer
        h_array = np.zeros([K*2,1])
        
        w_idx = 0
        for k in range(K):
            # x[k] , y[k]
            # make one step function
            # startX : calc raise point in [0,1]
            if k > 0:
                startX = self.x[k] +  (self.x[k-1] - self.x[k])/2
            else:
                startX = 0
    
            # endX : calc down point in [0,1]
            if k < K-1:
                endX = self.x[k] + (self.x[k+1] - self.x[k])/2
            else:
                endX = 1
    
            # calc b,w
            if k > 0:
                b,w = self.calc_b_w(startX,1)
            else:
                # init???
                b = 100
                w = 1
    
            # stepfunction 1stHalf
            #            __________
            # 0 ________|
            #        
            w_array[w_idx,0] = w
            w_array[w_idx,1] = b
            h_array[w_idx,0] = self.y[k]
            w_idx += 1
            
            # stepfunction 2ndHalf
            #        
            # 0 __________
            #             |________
            b,w = self.calc_b_w(endX,1)
            w_array[w_idx,0] = w
            w_array[w_idx,1] = b
            h_array[w_idx,0] = self.y[k]*-1
            
            # shape of 1st + 2nd is under wave
            #            _
            # 0 ________| |________
            #
            
            w_idx += 1
        
        # record param
        self.w = w_array
        self.h = h_array
        self.w_init = w_array[:,0]
        self.b_init = w_array[:,1]
        self.paramN = len(h_array)
        return
    
    # for bias coefficients setting
    def initB(self, shape, name=None):
        value = self.b_init
        value = value.reshape(shape)
        return K.variable(value, name=name)

    # for w coefficients (x) setting
    def initW(self, shape, name=None):
        value = self.w_init
        value = value.reshape(shape)
        return K.variable(value, name=name)
    
    # for h coefficients setting
    def initH(self, shape, name=None):
        value = self.h
        value = value.reshape(shape)
        return K.variable(value, name=name)

 
# deta making???
csv_input = pd.read_csv(filepath_or_buffer="data_out.csv",
                        encoding="ms932",
                        sep=",")

#Nombre d'éléments d'entrée (nombre de lignes)*Le nombre de colonnes) sera renvoyé.
print(csv_input.size)

#Renvoie l'objet DataFrame extrait uniquement pour la colonne spécifiée.
x = np.array(csv_input[["hour"]])
y = np.array(csv_input[["wind"]])
print(y.shape)


# num of records
N = len(x)

#Normalisation
x_max = np.max(x,axis=0)
x_min = np.min(x,axis=0)
x = (x - np.min(x,axis=0))/(np.max(x,axis=0) - np.min(x,axis=0))


# y >  2[m] : strong
# y <= 2[m] : weak
y_new = np.zeros(len(y),dtype=int)
for k in range(len(y)):
    if y[k] > 2:
        y_new[k] = 1

y_new = y_new.reshape(y.shape)
y = np.array(y_new,dtype=float)

# create InitInfo object
objInitInfo = InitInfo(x,y_orig)
# calc init value of w and h(and bias)
objInitInfo.calc_w_h()

#Créer un modèle d'apprentissage
model = Sequential()
#Couche entièrement connectée(1 couche->Couche XXX)
model.add(Dense(input_dim=1, output_dim=objInitInfo.paramN,
                bias=True,
                kernel_initializer=objInitInfo.initW,
                bias_initializer=objInitInfo.initB))

#Fonction d'activation(Fonction sigmoïde)
model.add(Activation("sigmoid"))

#Couche entièrement connectée(Couche XXX->2 couches)
model.add(Dense(output_dim=1,kernel_initializer=objInitInfo.initH))
#Fonction d'activation(fonction softmax)
model.add(Activation("sigmoid"))

sgd_ = keras.optimizers.SGD(lr=0.05)

cb = keras.callbacks.EarlyStopping(monitor='loss', 
                                    min_delta=0.0004, 
                                    patience=1, 
                                    verbose=0, 
                                    mode='auto', 
                                    baseline=None)

#Compilez le modèle
model.compile(loss="binary_crossentropy", optimizer=sgd_, metrics=["accuracy"])
#Effectuer l'apprentissage
model.fit(x, y, epochs=5000, batch_size=32, verbose=1,callbacks=[cb])

#Tracé de la valeur réelle
plt.plot(x,y,marker='x',label="true")
#Calculer les résultats Keras avec inférence,afficher
y_predict = model.predict(x)
#Graphique des résultats du calcul Keras
plt.plot(x,y_predict,marker='x',label="predict")
#Affichage de la légende
plt.legend()
# display result
print('OK per %.2f ' % (checkOKPercent(y_predict,y)*100.0))

Nous avons également ajouté un paramètre de rappel qui vous permet de quitter lorsqu'il est déterminé qu'il a convergé. Le résultat est ???

Figure 2020-05-05 203222.png

23/23 [==============================] - 0s 0us/step - loss: 0.2310 - acc: 1.0000
NG per 100.00 

Génial, le taux de réponse correcte est maintenant de 100%! !! !! Il s'est avéré que même la valeur initiale devait être bien définie. Cependant, il semble que ce n'est pas polyvalent? ?? ??

J'ai essayé de laisser tomber le coefficient au hasard, et cela semblait fonctionner avec les paramètres suivants.

# for bias coefficients setting
def initB(shape, name=None):
    L = np.prod(shape)
    value = np.ones(L).reshape(shape)*(-1000)
    return K.variable(value, name=name)

# for w coefficients (x) setting
def initW(shape, name=None):
    value = 1000/(np.random.random(shape))
    return K.variable(value, name=name)

C'est un coefficient à mettre de x à la couche intermédiaire, mais le côté Bias est fixé à 1000, le côté x (w) fait un nombre aléatoire de [0,1], et l'assigne de manière appropriée à 1000 / nombre aléatoire (plus de 1000) ( Je suis désolé si je me réveille à zéro pour cent).

import pandas as pd
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt

from keras.models import Sequential
from keras.layers import Activation, Dense
from keras.utils.np_utils import to_categorical
from keras import backend as K
import keras

# y:predict
# t:true
def checkOKPercent(y,t):
    # from predict param
    sign_newral = np.sign(np.array(y).reshape([len(t),1]) - 0.5)
    # from true param
    sign_orig = np.sign(np.array(t.reshape([len(t),1])) - 0.5)
    # are there same sign??
    NGCNT = np.sum(np.abs(sign_newral-sign_orig))/2
    # calc NG percent in [0.0-1.0]
    NGPer = NGCNT / len(t)
    # return OK percent [0.0-1.0]
    return 1.0-NGPer


# for bias coefficients setting
def initB(shape, name=None):
    L = np.prod(shape)
    value = np.ones(L).reshape(shape)*(-1000)
    return K.variable(value, name=name)

# for w coefficients (x) setting
def initW(shape, name=None):
    value = 1000/(np.random.random(shape))
    return K.variable(value, name=name)

 
# deta making???
csv_input = pd.read_csv(filepath_or_buffer="data_out.csv",
                        encoding="ms932",
                        sep=",")

#Renvoie l'objet DataFrame extrait uniquement pour la colonne spécifiée.
x = np.array(csv_input[["hour"]])
y = np.array(csv_input[["wind"]])

# num of records
N = len(x)

#Normalisation
x_max = np.max(x,axis=0)
x_min = np.min(x,axis=0)
x = (x - np.min(x,axis=0))/(np.max(x,axis=0) - np.min(x,axis=0))

# y >  2[m] : strong
# y <= 2[m] : weak
y_new = np.zeros(len(y),dtype=int)
for k in range(len(y)):
    if y[k] > 2:
        y_new[k] = 1

y_new = y_new.reshape(y.shape)
y = np.array(y_new,dtype=float)

#Créer un modèle d'apprentissage
model = Sequential()
#Couche entièrement connectée(1 couche->Couche XXX)
model.add(Dense(input_dim=1, output_dim=50,
                bias=True,
                kernel_initializer=initW,
                bias_initializer=initB))

#Fonction d'activation(Fonction sigmoïde)
model.add(Activation("sigmoid"))

#Couche entièrement connectée(Couche XXX->2 couches)
model.add(Dense(output_dim=1))
#Fonction d'activation(fonction softmax)
model.add(Activation("sigmoid"))

sgd_ = keras.optimizers.SGD(lr=0.3)

cb = keras.callbacks.EarlyStopping(monitor='loss', 
                                    min_delta=0.0001, 
                                    patience=1, 
                                    verbose=0, 
                                    mode='auto', 
                                    baseline=None)

#Compilez le modèle
model.compile(loss="binary_crossentropy", optimizer=sgd_, metrics=["accuracy"])
#Effectuer l'apprentissage
model.fit(x, y, epochs=5000, batch_size=32, verbose=1,callbacks=[cb])

#Tracé de la valeur réelle
plt.plot(x,y,marker='x',label="true")
#Calculer les résultats Keras avec inférence,afficher
y_predict = model.predict(x)
#Graphique des résultats du calcul Keras
plt.plot(x,y_predict,marker='x',label="predict")
#Affichage de la légende
plt.legend()
# display result
print('OK per %.2f ' % (checkOKPercent(y_predict,y)*100.0))

Le résultat était également bon.

Figure 2020-05-05 205259.png

Epoch 1032/5000
23/23 [==============================] - 0s 87us/step - loss: 0.1018 - acc: 1.0000
NG per 100.00 

Le nombre de nœuds dans la couche intermédiaire a été fixé à 50, mais dans de nombreux cas, le taux de réponse correcte n'a pas atteint 100%. Il semble que la stabilité augmentera en augmentant le nombre de nœuds (selon les résultats expérimentaux).

■ Lorsque le nombre de nœuds est de 150 Figure 2020-05-05 205718.png

Epoch 5000/5000
23/23 [==============================] - 0s 0us/step - loss: 0.0058 - acc: 1.0000
OK per 100.00 

Cette fois, j'ai implémenté une classification simple à deux classes dans Keras. Il semble y avoir un moyen d'utiliser la fonction softmax (l'article présenté au début était principalement implémenté dans softmax), donc je pensais l'écrire aussi, mais comme c'est devenu un peu plus, j'aimerais saisir une autre opportunité pense.

Recommended Posts

Jouons avec les données Amedas - Partie 1
Jouons avec les données Amedas - Partie 4
Jouons avec les données Amedas - Partie 3
Jouons avec les données Amedas - Partie 2
Jouons avec la 4e dimension 4e
Jouons avec Excel avec Python [Débutant]
Jouez avec Prophet
[Introduction à WordCloud] Jouez avec le scraping ♬
[Complément] [PySide] Jouons avec Qt Designer
Jouez avec PyTorch
Jouez avec CentOS 8
Jouez avec Pyramid
Jouez avec Fathom
Jeu à la main en Python (commençons avec AtCoder?)
[Piyopiyokai # 1] Jouons avec Lambda: création d'une fonction Lambda
Jouer avec Othello (Reversi)
[Jouons avec Python] Créer un livre de comptes de ménage
Jouons avec JNetHack 3.6.2 qui est plus facile à compiler!
[Piyopiyokai # 1] Jouons avec Lambda: créez un compte Twitter
[Piyopiyokai # 1] Jouons avec Lambda: création d'un script Python
Jouez avec les notifications push avec imap4lib
Jouez avec les partitions Linux
Faisons Othello avec wxPython
Jouer avec Jupyter Notebook (IPython Notebook)
[Python] Jouez avec le Webhook de Discord.
[Jouons avec Python] Traitement d'image en monochrome et points
Écrivons python avec cinema4d.
Faisons R-CNN avec Sklearn-theano
Construisons git-cat avec Python
Jouez avec le module MD de ASE
Jouez avec A3RT (texte suggéré)
[Jouons avec Python] Viser la génération automatique de phrases ~ Achèvement de la génération automatique de phrases ~
Téléchargeons des fichiers S3 avec CLI
Jouez avec une tortue avec des graphiques de tortue (partie 1)
Faisons une interface graphique avec python.
Jouez avec la série Poancare et SymPy
HTTPS avec Django et Let's Encrypt
Apprenons Deep SEA avec Selene
Faisons une rupture de bloc avec wxPython
Faisons l'IA d'Othello avec Chainer-Part 1-
Jouer avec l'implémentation de l'interface utilisateur Pythonista [Action implementation]
Jouez avec le module de capteur PIR [DSUN-PIR]
Jouez avec les partitions Linux ~ Suite ~
Faisons du scraping d'images avec Python
Faisons un graphe avec python! !!
Faisons un spacon avec xCAT
Faisons l'IA d'Othello avec Chainer-Part 2-
Spark play avec WSL anaconda jupyter (2)
Reconnaissons les émotions avec Azure Face
Jouez avec Turtle sur Google Colab
Jouez avec les démons parce que c'est une section
Analysons la voix avec Python # 1 FFT
Créons un modèle de reconnaissance d'image avec vos propres données et jouons!
[Jouons avec Python] Viser la génération automatique de phrases ~ Effectuer une analyse morphologique ~
Jouons avec le jeu de données d'analyse d'entreprise "CoARiJ" créé par TIS ②