👶

AI学習で有名なアヤメ(iris)の分類をPython初心者が実装してみる

2022/01/26に公開

はじめに

C言語を半年間学んだ私がPython初心者のまま機械学習を学んでみるという無謀な記事になっています。同じような境遇の方に少しでも参考になれば幸いです。

概要

TesorFlow/Kerasを用いてニューラルネットを作成し、3種類のアヤメの分類を行います。

Google Colabratory (以下colab) を使ってコードを書いていきます。
colabは環境構築が必要ないので、すぐにブラウザ上でPythonを動かすことができます。

https://colab.research.google.com/notebooks/welcome.ipynb?hl=ja

実装

まずはPythonの勉強から

さすがにPythonの大まかな書き方を知らなくてはコードは読めないので、以下のYoutubeで勉強しました。

https://youtu.be/HyU3XL2F9GE

データセットのロードと情報の確認

それではコードを書いていきます。
コードの左上にインタプリタと書かれているものを実行してください。
左上に出力結果と書かれているものはインタプリタで実行した出力結果になります。

まず、sklearn[1]モジュールのdatasetsからload_irisをインポートします。
そしてirisにアヤメのデータを格納します。

インタプリタ
from sklearn.datasets import load_iris
iris = load_iris()

irisにどんなデータが格納されたかdir関数で確認してみます。

インタプリタ
dir(iris)
出力結果
['DESCR',
 'data',
 'data_module',
 'feature_names',
 'filename',
 'frame',
 'target',
 'target_names']

DESCRがあるのでiris.DESCRを出力してデータセットの説明文を見てみます。

インタプリタ
print(iris.DESCR)
出力結果
.. _iris_dataset:

Iris plants dataset
--------------------

**Data Set Characteristics:**

    :Number of Instances: 150 (50 in each of three classes)
    :Number of Attributes: 4 numeric, predictive attributes and the class
    :Attribute Information:
        - sepal length in cm
        - sepal width in cm
        - petal length in cm
        - petal width in cm
        - class:
                - Iris-Setosa
                - Iris-Versicolour
                - Iris-Virginica
                
    :Summary Statistics:

    ============== ==== ==== ======= ===== ====================
                    Min  Max   Mean    SD   Class Correlation
    ============== ==== ==== ======= ===== ====================
    sepal length:   4.3  7.9   5.84   0.83    0.7826
    sepal width:    2.0  4.4   3.05   0.43   -0.4194
    petal length:   1.0  6.9   3.76   1.76    0.9490  (high!)
    petal width:    0.1  2.5   1.20   0.76    0.9565  (high!)
    ============== ==== ==== ======= ===== ====================

    :Missing Attribute Values: None
    :Class Distribution: 33.3% for each of 3 classes.
    :Creator: R.A. Fisher
    :Donor: Michael Marshall (MARSHALL%PLU@io.arc.nasa.gov)
    :Date: July, 1988

The famous Iris database, first used by Sir R.A. Fisher. The dataset is taken
from Fisher's paper. Note that it's the same as in R, but not as in the UCI
Machine Learning Repository, which has two wrong data points.

This is perhaps the best known database to be found in the
pattern recognition literature.  Fisher's paper is a classic in the field and
is referenced frequently to this day.  (See Duda & Hart, for example.)  The
data set contains 3 classes of 50 instances each, where each class refers to a
type of iris plant.  One class is linearly separable from the other 2; the
latter are NOT linearly separable from each other.

.. topic:: References

   - Fisher, R.A. "The use of multiple measurements in taxonomic problems"
     Annual Eugenics, 7, Part II, 179-188 (1936); also in "Contributions to
     Mathematical Statistics" (John Wiley, NY, 1950).
   - Duda, R.O., & Hart, P.E. (1973) Pattern Classification and Scene Analysis.
     (Q327.D83) John Wiley & Sons.  ISBN 0-471-22361-1.  See page 218.
   - Dasarathy, B.V. (1980) "Nosing Around the Neighborhood: A New System
     Structure and Classification Rule for Recognition in Partially Exposed
     Environments".  IEEE Transactions on Pattern Analysis and Machine
     Intelligence, Vol. PAMI-2, No. 1, 67-71.
   - Gates, G.W. (1972) "The Reduced Nearest Neighbor Rule".  IEEE Transactions
     on Information Theory, May 1972, 431-433.
   - See also: 1988 MLC Proceedings, 54-64.  Cheeseman et al"s AUTOCLASS II
     conceptual clustering system finds 3 classes in the data.
   - Many, many more ...

大量に出力されましたが注目すべき文は以下の説明です。

出力結果(要約)
    :Number of Instances: 150 (50 in each of three classes)
    :Number of Attributes: 4 numeric, predictive attributes and the class
    :Attribute Information:
        - sepal length in cm
        - sepal width in cm
        - petal length in cm
        - petal width in cm
        - class:
                - Iris-Setosa
                - Iris-Versicolour
                - Iris-Virginica

この説明からSetosa,Versicolour,Virginicaの3つのクラスがあり、それぞれ50個のデータを持ち、合計で150個のデータがあることがわかります。

またそのクラス一つの中に4つのデータが格納されており、それぞれ

  • がく片の長さ(cm)
  • がく片の幅(cm)
  • 花弁の長さ(cm)
  • 花弁の幅(cm)

のデータであることが説明文に書いてあります。
これらの長さや幅を用いてアヤメを分類するAIを作ります。

また分類するアヤメの種類はtarget_namesに格納されており、

インタプリタ
list(iris.target_names)

を実行することで確認することもできます。

それでは計測データの構造をshapeメソッドを使って確認します。

インタプリタ
iris.data.shape
出力結果
(150, 4)

150行、4列であることがわかりました。具体的な値を確認します。

インタプリタ
iris.data
出力結果
array([[5.1, 3.5, 1.4, 0.2],
       [4.9, 3. , 1.4, 0.2],
       [4.7, 3.2, 1.3, 0.2],
       [4.6, 3.1, 1.5, 0.2],
       ・・・・
       [6.3, 2.5, 5. , 1.9],
       [6.5, 3. , 5.2, 2. ],
       [6.2, 3.4, 5.4, 2.3],
       [5.9, 3. , 5.1, 1.8]])

各列に何が格納されているかはfeature_namesに入っています。

インタプリタ
iris.feature_names
出力結果
['sepal length (cm)',
 'sepal width (cm)',
 'petal length (cm)',
 'petal width (cm)']

よってiris.dataには、各行ごとに左から
[がく片の長さ(cm)、がく片の幅(cm)、花弁の長さ(cm)、花弁の幅(cm)]
が入っていることがわかりました。

教師データ(ターゲットデータ)はiris.targetに格納されています。
まずはiris.targe同様、shapeメソッドを使って構造を確認してみましょう。

インタプリタ
iris.target.shape
出力結果
(150,)

教師データは1次元配列で150個の要素を持っていることが確認できました。
具体的な中身を確認します。

インタプリタ
iris.target
出力結果
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])

計測データに対応する、0,1,2のそれぞれ3種類の数値が格納されており、それぞれ順番に

  • 0:Setosa
  • 1:Versicolour
  • 2:Virginica

です。

またこのことから最初の50個がSetosa、次の50個がVersicolour、次の50個がVirginicaであることがわかったのでそれを活用して次は散布図を作成します。

データを可視化する

matplotlibについては説明が長くなってしまうので以下の記事を参考にしてください。

https://www.delftstack.com/ja/howto/matplotlib/scatter-plot-legend-in-matplotlib/

https://qiita.com/kakiuchis/items/798c00f54c9151ab2e8b

また[:50, 0]といった記述のことをスライスと言いますが、こちらについてもわからない方は以下の記事を参考にしてみてください。

https://hibiki-press.tech/python/slice/1

それではコードを書いていきます。
iris.dataには4種類の計測データがありますが、
x軸にはがく片の長さ、y軸にはがく片の幅が来るように散布図を書いてみます。

インタプリタ
import matplotlib.pyplot as plt

x = iris.data
y = iris.target

plt.scatter(x[:50, 0], x[:50, 1], color='r', marker='o', label='setosa')
plt.scatter(x[50:100, 0], x[50:100, 1], color='g', marker='+', label='versicolor')
plt.scatter(x[100:, 0], x[100:, 1], color='b', marker='x', label='virginica')
plt.title("Iris Plants Database")
plt.xlabel("sepal length(cm)")
plt.ylabel("sepal width(cm)")
plt.legend()
plt.show


図1. がく片の長さと幅の関係

散布図を見てみると、がく片の長さと幅だけでもそれぞれの特徴を認識することができます。

TensorFlowとKerasのロード

では、これから機械学習を行う上で必要なモジュールであるTensorFlowとKerasをロードします。

インタプリタ
import tensorflow as tf
from tensorflow import keras
print(tf.__version__)
tf.random.set_seed(1)
出力結果
2.7.0

無事インポートできていればTensorFlowのバージョンが出力されるはずです。

また最後の行は乱数のシード値を固定しています。
引数は実数であればなんでもいいです。
ちなみにこのシード値に結果を左右されるのはあまりよろしくありません。
今回は1に設定します。

本当に固定されているのか検証してみた

tf.random.uniform:一様分布からランダムに値をサンプリングする関数

インタプリタ
tf.random.set_seed(0)
print(tf.random.uniform([1]))
tf.random.set_seed(0)
print(tf.random.uniform([1]))
出力結果
tf.Tensor([0.29197514], shape=(1,), dtype=float32)
tf.Tensor([0.29197514], shape=(1,), dtype=float32)

これによってTensorFlowの重み[2]の設定が固定されました。
固定する理由としては、科学にとって大切な再現性を厳守するためです。
しかしこの行は別に入れても入れなくても好みです。
毎回違う結果を見たいのであれば最後の行は削除していただいても構いません。

データセットの分割

ではデータセットの分割を行う前にデータをランダムに並べ替えます。

インタプリタ
import random

random.seed(12345)
Ndata = len(iris.data)
print(f"Ndata={Ndata}")
idxr = [k for k in range(Ndata)]
print(idxr)
random.shuffle(idxr)
print(idxr)
出力結果
Ndata=150
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149]
[109, 78, 108, 60, 93, 46, 14, 59, 118, 8, 18, 96, 77, 62, 104, 25, 99, 133, 130, 82, 80, 148, 17, 4, 34, 101, 33, 81, 126, 61, 28, 89, 132, 79, 9, 68, 64, 54, 149, 35, 26, 136, 70, 140, 40, 57, 120, 39, 30, 119, 139, 113, 102, 103, 86, 45, 72, 114, 50, 11, 122, 51, 24, 10, 56, 7, 6, 117, 124, 145, 112, 121, 144, 137, 91, 128, 1, 38, 21, 92, 16, 138, 55, 75, 32, 100, 85, 71, 27, 63, 15, 97, 5, 131, 135, 147, 67, 29, 127, 146, 134, 73, 87, 13, 116, 83, 12, 36, 22, 74, 98, 20, 143, 84, 115, 0, 53, 141, 88, 65, 123, 125, 58, 3, 142, 43, 107, 48, 19, 52, 37, 42, 129, 105, 23, 90, 47, 44, 66, 110, 31, 95, 41, 111, 69, 49, 94, 76, 2, 106]

まずrandom.seed(12345)で再現性のためにseed値を固定します。先ほど同様引数は実数であればなんでも構いません。今回は12345に設定しました。

len関数を使い、Ndata = len(iris.data)でデータの要素数を調べ、
print(f"Ndata={Ndata}")で出力しています。

idxr = [k for k in range(Ndata)]この書き方はPythonを勉強していないと初見ではわからないと思います。これはリスト内包表記[3]と言います。

リスト内包表記を簡単に書いた場合
idxr = []
for k in Ndate
    idxr.append(k)

print(idxr)を実行することでリスト内に0から149までの数値が格納されていることがわかります。
ちなみにidxrとはインデクサー(indexer)のことでプログラミングではこのように略して書くことが多いようです。

またrandom.shuffle(idxr)よって先ほどのリストの中身をシャッフルし、
print(idxr)で中身がランダムに再配置されたか確認しています。

それでは以下のコードでデータの分割を行います。わかりやすくするために今回は訓練データと検証データを半分、半分で分割します。

インタプリタ
Ndata_train=int(Ndata*0.5)
print(f"# of training data = {Ndata_train}")
print(f"# of validation data = {Ndata-Ndata_train}")

train_data = iris.data[idxr[:Ndata_train]]
train_labels = iris.target[idxr[:Ndata_train]]

val_data = iris.data[idxr[Ndata_train:]]
val_labels = iris.target[idxr[Ndata_train:]]
出力結果
# of training data = 75
# of validation data = 75

Ndata_train=int(Ndata*0.5)でデータ分割の割合を指定しています。

なぜintにキャスト(型変換)しているのか
インタプリタ
print(type(Ndata))
print(type(Ndata*0.5))
print(type(int(Ndata*0.5)))
出力結果
<class 'int'>
<class 'float'>
<class 'int'>

float型からわざわざint型に戻しています。もしint型じゃなければ
train_data = iris.data[idxr[:Ndata_train]]のスライスを実行する行で

TypeError: slice indices must be integers or None or have an __index__ method

とインタプリタ先生に怒られてしまいます。スライスはintでなければならないようです。

print(f"# of training data = {Ndata_train}")
print(f"# of validation data = {Ndata-Ndata_train}")
それぞれ訓練データと検証データの数を出力しています。

train_data = iris.data[idxr[:Ndata_train]]の行では訓練データを、
train_labels = iris.target[idxr[:Ndata_train]]の行では訓練データの教師ラベルをスライスを使って、
idxrの0~74番目をそれぞれiris.datairis.targetに対応させて、train_datatrain_labelsに代入しています。

上と同じくval_data = iris.data[idxr[Ndata_train:]]の行では検証データを、
val_labels = iris.target[idxr[Ndata_train:]]の行では検証データの教師ラベルをスライスを使って、
idxrの75~149番目をそれぞれiris.datairis.targetに対応させて、
val_dataval_labelsに代入しています。

ニューラルネットワークの設計

それではニューラルネットワークの作成をしていきます。

インタプリタ
model = keras.Sequential([
    keras.layers.Dense(4, activation='relu'),
    keras.layers.Dense(10, activation='relu'),
    keras.layers.Dense(10, activation='relu'),
    keras.layers.Dense(3, activation='softmax')
])

KerasのSequentialというクラスを使ってニューラルネットワークを作成しています。
4次元(がく片の長さ、がく片の幅、花弁の長さ、花弁の幅)のデータから3次元(setosa, versicolor, virginica)のラベルへ層が構成されています。
中間層は2層でユニット数(中間層の次元)は10、Dense[4]で設定したため全結合層になっています。
modelには、keras.Sequentialから作成したインスタンスが入っており、これを使用してデータを学習させることが可能になります。

機械学習を行う

まずはmodelインスタンスのcompileメソッドを使って学習の設定を行います。

インタプリタ
model.compile(optimizer='SGD',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

compileには引数が3つあります。

  • optimizer:最適化手法を設定する。今回はSGDに設定。
  • loss:損失関数を設定する。今回はsparse_categorical_crossentropyに設定。
  • metrics:評価関数を設定する。基本的にはここにaccuracyを入れておけば問題ない。自作の評価関数を設定しても良い。

損失関数や評価関数などはこちらのサイトが参考になりました↓

https://zenn.dev/nekoallergy/articles/machinelearning-func

それでは学習を行います。modelインスタンスのfitメソッドを使うことで学習を行うことができます。

インタプリタ
training_history = model.fit(train_data, train_labels,
                             validation_data=(val_data, val_labels),
                             epochs=30,
                             batch_size = Ndata_train//10,
                             verbose=1)
出力結果
Epoch 1/30
11/11 [==============================] - 1s 18ms/step - loss: 1.1173 - accuracy: 0.3600 - val_loss: 1.0531 - val_accuracy: 0.3067
Epoch 2/30
11/11 [==============================] - 0s 5ms/step - loss: 1.0454 - accuracy: 0.3467 - val_loss: 1.0474 - val_accuracy: 0.3867
Epoch 3/30
11/11 [==============================] - 0s 4ms/step - loss: 1.0529 - accuracy: 0.3733 - val_loss: 1.0373 - val_accuracy: 0.5067
・・・・
Epoch 28/30
11/11 [==============================] - 0s 6ms/step - loss: 0.4374 - accuracy: 0.8800 - val_loss: 0.4318 - val_accuracy: 0.7200
Epoch 29/30
11/11 [==============================] - 0s 4ms/step - loss: 0.4376 - accuracy: 0.8000 - val_loss: 0.3903 - val_accuracy: 0.9333
Epoch 30/30
11/11 [==============================] - 0s 4ms/step - loss: 0.4205 - accuracy: 0.8800 - val_loss: 0.3966 - val_accuracy: 0.8000

fitの引数には、訓練データとそれに対応する訓練データのラベルを代入しています。
また今回はオプションとして他に4つの引数を使っています。

  • validation_data:検証用データをタプルにして渡す。今回は(val_data, val_labels)を設定している。
  • epochs:エポック数を設定。今回は30に設定している。デフォルトは1。
  • batch_size:バッチサイズを設定。今回ミニバッチのサイズはNdata_train//10によって7つだが、この数字に特に意味はない。デフォルトは32。
  • verbose:ログ出力の設定。0だとログが出ない、正の値だと細かいログが出力され、負の値だとepoch数のみ表示される。デフォルトは1。

結果の評価

fitの返り値をtraining_historyに代入したため、
学習の履歴を見るためにtraining_historyの中身を見ていきましょう。

インタプリタ
dir(training_history)
出力結果
['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 '_chief_worker_only',
 '_implements_predict_batch_hooks',
 '_implements_test_batch_hooks',
 '_implements_train_batch_hooks',
 '_keras_api_names',
 '_keras_api_names_v1',
 '_supports_tf_logs',
 'epoch',
 'history',
 'model',
 'on_batch_begin',
 'on_batch_end',
 'on_epoch_begin',
 'on_epoch_end',
 'on_predict_batch_begin',
 'on_predict_batch_end',
 'on_predict_begin',
 'on_predict_end',
 'on_test_batch_begin',
 'on_test_batch_end',
 'on_test_begin',
 'on_test_end',
 'on_train_batch_begin',
 'on_train_batch_end',
 'on_train_begin',
 'on_train_end',
 'params',
 'set_model',
 'set_params',
 'validation_data']

大量にでてきたましたが学習の履歴はhistoryに格納されてそうですね。

インタプリタ
training_history.history
出力結果
{'accuracy': [0.36000001430511475,
  0.3466666638851166,
  0.3733333349227905,
  ・・・・
  0.43177515268325806,
  0.3902606964111328,
  0.39655089378356934]}

出力結果に'accuracy':とあることからdict型のリストであることがわかりました。。
辞書のキーに何が入ってるのか確認するためにkeysメソッドを使います。

インタプリタ
training_history.history.keys()
出力結果
dict_keys(['loss', 'accuracy', 'val_loss', 'val_accuracy'])

4つのキーで構成されていることがわかりました。

  • loss:訓練データに対する損失関数の値
  • accuracy:訓練データに対する精度の値
  • val_loss:検証データに対する損失関数の値
  • val_accuracy:検証データに対する精度の値

それでは最終的な、訓練データに対する精度の値と検証データに対する精度の値を確認してみましょう。

インタプリタ
print("traininig")
print(training_history.history['accuracy'][-1])
print("validation")
print(training_history.history['val_accuracy'][-1])
出力結果
traininig
0.8799999952316284
validation
0.800000011920929

訓練データに対する精度の値が88%、検証データに対する精度の値が80%であることがわかりました。

最後に損失関数と精度の履歴について可視化したいと思います。

インタプリタ
# 訓練データに対する損失関数のプロット
y=training_history.history['loss']
x=range(len(y))
plt.semilogy(x,y,label="loss for training")

# 検証データに対する損失関数のプロット
y=training_history.history['val_loss']
x=range(len(y))
plt.semilogy(x,y,label="loss for validation",alpha=0.5)

plt.legend()
plt.xlabel("Steps")
plt.show()

# 訓練データに対する精度のプロット
y=training_history.history['accuracy']
x=range(len(y))
plt.plot(x,y,label="accuracy for training")

# 検証データに対する精度のプロット
y=training_history.history['val_accuracy']
x=range(len(y))
plt.plot(x,y,label="accuracy for validation")

plt.legend()
plt.xlabel("Steps")
plt.ylim(0,1.1)
plt.show()


図2. アヤメの分類の機械学習の履歴。上が損失関数の履歴で、下が精度の履歴。

損失関数の散布図は対数軸にするためにplt.semilogyを使っています。
精度の散布図はplt.ylimを使ってy軸の範囲を0~1.1に設定しています。

結論

最終的な訓練データの正答率は88%、検証データの正答率は80%でした。
アヤメは3種類なので当てずっぽうの33.3%よりは高い精度となりました。
あまり良い精度ではありませんが、ひとまず機械学習成功?と言えるでしょう。

最後の図2では機械学習の目標通り、損失関数が減少すると分類精度は上昇するような負の相関が見られました。

考察

今回のセットアップは初学者用なので変更・改善の余地が多くあります。

  • データの分割の仕方、半分半分に分けずに訓練データをもっと多く取ってみる。
  • 中間層の数やユニット数を増やしてみる。
  • 全結合層だけでなく他の層を試したり、relu関数以外の他の活性化関数も使ってみる。
  • 最適化アルゴリズムをSGDではなく、Adamなど別のものに変更してみる。
  • 損失関数をsparse_categorical_crossentropyではなく別のものに変更してみる。
  • エポック数を増やしたり、バッチサイズを変更してみる。

これらを試したからといって必ずしも精度が上がるわけではありませんが、検証する価値はあると思います。ぜひコードをコピペして遊んでみてください。

感想

長い記事を読んでいただきありがとうございました。

実はコードよりも、そもそもSGDとは何か、交差エントロピーとは何か...といった数学的な問題に時間がかかりました。
微分・線形代数・確率統計など大学数学の知識連発で、高校生の時に理系を目指した自分に感謝しまくりです。

しかし理系と言えど数学が苦手なのでどこまで太刀打ちできるかわかりません。
いずれGANや強化学習もやってみたいので自分の脳スペックと相談しながら気ままに勉強したいと思います。

最後に、初心者が必死こいて殴り書いた記事ですが何かの役に立てばうれしいです。

参考

https://bookclub.kodansha.co.jp/product?item=0000348001

http://www.sotechsha.jp/pc/html/1167.htm

https://keras.io/ja/getting-started/sequential-model-guide/

https://child-programmer.com/ai/keras/

脚注
  1. sci-kit learnは機械学習のためのモジュールであり、アヤメ以外にも多くのデータセットが含まれています。 ↩︎

  2. https://qiita.com/bee2/items/08eab7a899c9ff56eb35 ↩︎

  3. https://www.yoheim.net/blog.php?q=20150702 ↩︎

  4. https://sinyblog.com/deaplearning/keras_how_to/#:~:text=説明-,Dense,-(keras.layers.Dense) ↩︎

Discussion