<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>機械学習 &#8211; 機械学習 入門コースの決定版!機械学習エンジニアを目指すならcodexa（コデクサ）</title>
	<atom:link href="https://www.codexa.net/category/machine-learning/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.codexa.net</link>
	<description>毎日1400名以上のエンジニアが利用してる機械学習 入門コースの決定版！機械学習に必要な線形代数や統計基礎、Pythonライブラリなどの基礎コースも無料で公開中！</description>
	<lastBuildDate>Wed, 07 Sep 2022 09:07:06 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=5.7.7</generator>

<image>
	<url>https://www.codexa.net/wp-content/uploads/2017/11/cropped-favicon-32x32.png</url>
	<title>機械学習 &#8211; 機械学習 入門コースの決定版!機械学習エンジニアを目指すならcodexa（コデクサ）</title>
	<link>https://www.codexa.net</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>交差検証（Python実装）を徹底解説！図解・サンプル実装コードあり</title>
		<link>https://www.codexa.net/cross_validation/</link>
					<comments>https://www.codexa.net/cross_validation/#respond</comments>
		
		<dc:creator><![CDATA[codexaチーム]]></dc:creator>
		<pubDate>Wed, 07 Sep 2022 08:50:00 +0000</pubDate>
				<category><![CDATA[機械学習]]></category>
		<guid isPermaLink="false">https://www.codexa.net/?p=3200</guid>

					<description><![CDATA[交差検証（クロスバリデーション ）とはデータの解析と評価を交差させることで、より正確な推定値を求める手法です。Pythonの実装コードサンプルを使って交差検証の理解を深めましょう。]]></description>
										<content:encoded><![CDATA[<p>何かを「正しい」と判断するとき、読者の皆様は何を根拠としますか。例えば、経験を元にする場合、一度だけの経験では偶然の可能性も考えられます。これが複数の経験からの判断であれば、少しは信憑性が増すと思います。一度の結果では信憑性が薄くとも、多くの結果を得られればある程度の精度で判断できる材料となります。</p>
<p>機械学習も同様です。様々な特徴量を扱う機械学習では、予測モデル（以下：モデル）の評価が本当に「正しい」のかを判断することは難しい問題です。特に、過学習などの問題は機械学習の技術が発展している近年でも残り続けています。</p>
<p>本稿では、機械学習を扱う上で重要な交差検証（クロスバリデーション ）について解説します。前半ではデータ分割の基本と、交差検証の定義を解説をします。後半では実際のデータセットを用いて交差検証の実装を行います。交差検証は様々な場面で紹介されていますが、実際に学ぶと詰まるポイントが多いようにも感じます。そのため、本稿では初学者の方でも理解できるように、できる限り直感的に解説していきます。</p>
<h3>データ分割</h3>
<p>交差検証の内容に入る前に、データ分割（Data Split）について解説します。この概念は交差検証を理解する上で必要なものです。もし、データ分割について少しでも不安のある方は必ず確認するようにしてください。本稿の主題はあくまで交差検証ですので、データ分割の解説は基礎的な部分のみを扱います。</p>
<h4>訓練・検証・テストの定義</h4>
<p>訓練データ（train data）、検証データ（validation data）、テストデータ（test data）の定義について解説します。本稿では基本的に以下の定義を使用します。しかし、解説の中で用途が変化する場合があります。その際は都度解説しています。読者の皆様は一旦以下の定義を確認しておいてください。</p>
<ul>
<li>訓練データ：モデルの学習に使用されるデータです。学習データとも呼ばれることもあります。</li>
<li>検証データ（データセットが1つの場合）：モデルのハイパーパラメータの調整に使用するデータです。</li>
<li>検証データ（コンペティションの場合）：手元でモデルの評価を確認するためのデータです。</li>
<li>テストデータ：訓練データを用いて構築されたモデルの評価に使用されます。コンペティションで提出（サブミット）を行うデータです。</li>
</ul>
<blockquote><p>ハイパーパラメータ（英語：Hyperparameter）とは機械学習アルゴリズムの挙動を設定するパラメータをさします。ハイパーパラメータの基本的な内容に関しては本稿で扱うと長くなってしまうため省略しています。過去にAIマガジンでハイパーパラメータに関する記事が存在します。本稿でのハイパーパラメータもこの記事をベースとしています。これらの知識に関して不安のある方は以下の記事を読まれることをお勧めします。（参考：<a href="https://www.codexa.net/hyperparameter-tuning-python/" target="_blank" rel="nofollow noopener">ハイパーパラメータとは？チューニングの手法を徹底解</a>）</p></blockquote>
<h4>訓練・テストデータの役割</h4>
<p>機械学習は与えられたデータセットからモデルを作成します。次に、モデルが未知のデータに対してどの程度の精度を持つかを評価します。そのため、与えられたデータセットを訓練データとテストデータに分割する必要があります。図1はデータ分割を示しています。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/08/画像1.png" alt="" /><br />
図1のようにデータ分割を行うことで、モデルの評価を行うことができます。当然ですが、使用された訓練データはテストデータとして扱うことはできません。モデルは訓練データを用いて学習していますので、同じデータに対する評価を行いえば精度は必然的に高くなるからです。</p>
<h4>検証データの役割</h4>
<p>定義でも述べた通り検証データはハイパーパラメータの調整に使用されます。サポートベクターマシン（以下：SVM）やランダムフォレスト（Random Forest）のような、モデル自体の重みの他にハイパーパラメータを持つモデルは、訓練データだけでは最適化することができません。その時に登場するのが検証データです。図2は検証データも使用する場合のデータ分割を示しています。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/08/画像2.png" alt="" /><br />
図2ではデータセットが3分割されています。まず、訓練データで各パラメータに対するモデルの学習を行います。次に、検証データで各パラメータで学習したモデルの評価を行い、最も適切なパラメータを持つモデルを選択します。最後に、選択されたモデルを用いて、テストデータに対する評価を行います。図3にはハイパーパラメータの調整の流れが示されています。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/08/画像3.png" alt="" /><br />
よくある疑問として、検証データとテストデータは同一ではダメなのかというものがあります。結論からするとダメです。ハイパーパラメータは検証データに対するモデルの評価によって決定されます。よって、最終的に決定されたハイパーパラメータは検証データのみに適合された可能性が存在します。そのため、最終的な評価はテストデータを利用して行うことが求められます。</p>
<p>本稿ではグリッドサーチ（Grid Search）と呼ばれるハイパーパラメータの探索法を基準としています。ハイパーパラメータの探索方法にはいくつかの手法が存在します。先ほど紹介した<a href="https://www.codexa.net/hyperparameter-tuning-python/" target="_blank" rel="nofollow noopener">こちら</a>の記事に解説されています。確認してみてください。</p>
<h4>コンペティションでの注意点</h4>
<p>データ分割を理解する上でkaggleなどのデータ分析コンペティション（以下：コンペティション）との違いに注意してください。定義自体が曖昧なため、完璧に理解するのは難しいかもしれません。しかし、本稿を読み進める上でも注意してください。まず、データセットの提供には主に2つの形式があります。</p>
<ul>
<li>全てラベルが存在する状態で与えられる場合（研究など）</li>
<li>ラベルが存在するデータと存在しないデータが別々に与えられる場合（コンペティションなど）</li>
</ul>
<p>まず、研究などではデータセット全てにラベルがある状態で提供される場合が多いです。そこからデータを分割し、機械学習モデルの学習から評価までを行います。対してコンペティションなどでは、データがあらかじめ分割された状態で提供される場合が多いです。例としてはtrain.csvで学習した予測モデルを用いてtest.csvの予測値をサブミットするなどが挙げられます。このとき、test.csvの方にはラベルはありません。サブミット時に初めて評価がわかります。図4は両者の違いを示しています。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/08/画像4.png" alt="" /><br />
コンペティションの場合、検証・テストデータの区別が曖昧になります。筆者の肌感では、test.csvがテストデータと呼ばれることが多いように思います。そのため、本稿ではtest.csvなどはテストデータとし、train.csvで分割されたデータに関しては、訓練データと検証データとしています。本稿での解説が他の記事と異なる可能性があります。本稿を他の記事と比較される際は言葉の定義に違いがないかをよく確認するようにしてください。</p>
<h3>交差検証（クロスバリデーション ）</h3>
<p>それでは交差検証（Cross-Validation）について解説していきます。交差検証とはデータの解析と評価を交差させることで、より正確な推定値を求める手法です。交差検証の定義はこれだけなのですが、実際の使われ方にはいくつかの種類が存在します。しかし、大きな違いではないので一度理解できれば対応できると思います。</p>
<h4>モデルの汎化性能を評価する交差検証</h4>
<p>こちらが筆者の知る限り最も一般的な交差検証です。前節ではモデルを評価するためにデータ分割を行うと解説しました。しかし、2分割での評価を1度行っただけでは、そのテストデータが偶然予測しやすかった可能性が存在します。そこで、訓練データとテストデータを交差させ、それぞれの評価の平均を取得することでより正しい推定値を獲得します。この交差検証は主に研究などの1つのデータセットで学習から評価までを行わなければならない場合に用いられます。図5は5分割の交差検証を示しています。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/08/画像5.png" alt="" /></p>
<p>全てのデータが一度は訓練・テストデータ両方に使われていることがわかると思います。これにより、テストデータが偶然に予測しやすいなどの状態を避けることができます。注意点としては各fold毎にモデルは作り直す必要があることです。各foldの訓練データの中には、他のfoldのテストデータの情報が含まれています。1つのモデルを使用した場合、2fold目以降はテストデータを知っている状態で学習を行うことになります。それでは交差検証の意味がなくなりますので注意してください。</p>
<h4>ハイパーパラメータチューニングのための交差検証</h4>
<p>次にハイパーパラメータチューニングのための交差検証です。ハイパーパラメータの調整には検証データを用います。そのため、1つの検証データだけでは、最適な値か判断できない場合があります。そのため、交差検証を用いて一番評価の良いハイパーパラメータに調整します。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/08/画像6.png" alt="" /></p>
<p>図6は図5と違う点が存在します。まずはテスト交差検証とは別にテストデータが存在する点です。この理由は図3の時と同様です。今回の交差検証は各foldの検証データに対して、最も良い評価であるハイパーパラメータを探索しています。つまり、検証データに対する評価の平均では、実際よりも高く見積もられる可能性があります。そのため、テストデータが別で用意することが前提になります。</p>
<h4>コンペティションで行われる交差検証</h4>
<p>最後にコンペティションで用いられる交差検証についてです。あくまで一例ですので、全ての分析者がこの方法を用いるわけではありません。コンペティションの内容によっても変化します。各コンペティションでの最適な交差検証は、ディスカッションなどを参考にしながら進めるようにしてください。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/08/画像7.png" alt="" /><br />
交差検証自体はハイパーパラメータの調整の時と大きな違いはありません。違いはモデルの保存が必要ということです。コンペティションではテストデータのラベルが存在しないため、交差検証時に各foldのモデルを保存します。同じテストデータに対して各モデルでアンサンブルした予測値をサブミットします。サブミット数には制限がある場合もあります。そのため、ある程度までは訓練データと検証データを用いた交差検証でモデルの良し悪しを判断します。</p>
<h3>交差検証の種類</h3>
<p>ここまで、交差検証の概要について解説してきました。ここからは交差検証の種類について解説します。ここでいう種類とはデータ分割の仕方を指します。データ分割の仕方を間違えるとテストデータや検証データに対する予測が正確に行われない可能性があります。各交差検証の必要性については各節で解説します。また、本節では理解しやすいように、訓練データとテストデータのみで解説を進めます。コンペティションなどに応用したい方はテストデータを検証データと思っていただければ問題ありません。</p>
<p>本稿はGoogle Colabを用いて実装していきます。ライブラリをそのままインポートすれば同じように実装可能です。是非、ご自身でも実装してみてください。Google Colabを使用したことがない方は下記の記事を参考にしてください。（参考：<a href="https://www.codexa.net/how-to-use-google-colaboratory/" target="_blank" rel="noopener">Google Colabの知っておくべき使い方 – Google Colaboratoryのメリット・デメリットや基本操作のまとめ</a>）</p>
<p>先に今回使用するデータセットの作成をします。データセットについてですが、dataには通常の文字列、labelには目的変数、groupにはdataが属しているグループが示されています。細かくはコード内に記載してありますので確認してください。</p><pre class="crayon-plain-tag">#[IN]:

#ライブラリのインポート
import numpy as np
import pandas as pd

#データの準備
all = dict(
    #分割する文字列
    data = ["0","1","2","3","4","5","6","7","8","9"],
    #目的変数
    label = [1,1,1,1,1,0,0,0,0,0],
    #dataが所属するグループ
    group = [1,0,1,0,1,0,1,0,1,0],
    ) 
#DataFrameを作成
df = pd.DataFrame(data = all)
#各列を抽出
data = df["data"]
label = df["label"]
group = df["group"]</pre><p>
<h4>ホールドアウト</h4>
<p>ホールドアウト（Holdout）法は最もシンプルな方法です。本稿では交差検証と一緒に解説していますが、実際には交差検証には含まれません。これはホールドアウト法が交差を行なっていないためです。ホールドアウト法はデータセット全体をランダムに分割することで行えます。交差検証は分割した分だけ学習時間が長くなる傾向があるため、短い時間で判断する場合にはホールドアウト法は有効です。実装も非常に簡単ですので、初学者の方にオススメです。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/08/画像8.png" alt="" /></p>
<p>実装はscikit-learnに用意されているtrain_test_split()を使用しします。train_test_split()はデータと目的変数を与え、引数を指定することで指定した割合で分割することができます。今回は2割をテストデータとします。重要な引数に関しては以下に記載します。詳細については公式ドキュメントを参照してください。（参考：<a href="https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html" target="_blank" rel="noopener">scikit-learn公式：train_test_split</a>）</p>
<ul>
<li>train_size：訓練データの割合</li>
<li>test_size：テストデータの割合</li>
<li>shuffle：シャッフルの有無</li>
<li>random_state：シード値</li>
</ul>
<pre class="crayon-plain-tag">#[IN]:

#train_test_splitのインポート
from sklearn.model_selection import train_test_split

#データ分割
train_x, test_x, train_y, test_y = train_test_split(data, label, train_size = 0.8 ,test_size = 0.2, shuffle = True, random_state = 0)

#訓練データの確認
print(train_x.values)
print(train_y.values)
#テストデータの確認
print(test_x.values)
print(test_y.values)</pre>
<pre class="crayon-plain-tag">#[OUT]

['4' '9' '1' '6' '7' '3' '0' '5']
[1 0 1 0 0 1 1 0]
['2' '8']
[1 0]</pre>
訓練データとテストデータが8：2に分割されていることがわかると思います。train_sizeとtest_sizeの合計は必ずしも1である必要はありません。合計が1未満であれば、データセットの一部分を使用せずにデータ分割を行います。</p>
<h4>K分割交差検証（K-Fold Cross-Validation）</h4>
<p>K分割交差検証（以下K-Fold）は最も一般的な交差検証です。データをK個に分割し、K-1個を訓練データ、1個をテストデータとします。データを交差させながらK回行い、得られた結果の平均値を求めます。前節で解説したように、モデルの評価の正しい推定値を得るために使用されます。図9はK-Foldを示しています。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/08/画像9.png" alt="" /></p>
<p>実装はscikit-learnに用意されているKFold()を使用しします。KFold()は分割数を引数に指定することができます。交差検証は、split()メソッドをfor文で扱うことで実装できます。for文内のtrainとtestは分割された際のindex値です。今回は5分割で実装します。重要な引数に関しては以下に記載します。詳細については公式ドキュメントを参照してください。（参考：scikit-leanr公式：KFold）</p>
<ul>
<li>n_splits：分割数</li>
<li>shuffle：シャッフルの有無</li>
<li>random_state：シード値</li>
</ul>
<pre class="crayon-plain-tag">#[IN]:

#KFoldのインポート
from sklearn.model_selection import KFold

#KFoldの設定
kf = KFold(n_splits = 5, shuffle = True, random_state = 1)

#交差検証
for train, test in kf.split(df):
  print(f"訓練データは：{data[train].values}, テストデータは：{data[test].values}")
  print(f"訓練ラベルは：{label[train].values}, テストラベルは：{label[test].values}")</pre>
<pre class="crayon-plain-tag">#[OUT]

訓練データは：['0' '1' '3' '4' '5' '6' '7' '8'], テストデータは：['2' '9']
訓練ラベルは：[1 1 1 1 0 0 0 0], テストラベルは：[1 0]
訓練データは：['0' '1' '2' '3' '5' '7' '8' '9'], テストデータは：['4' '6']
訓練ラベルは：[1 1 1 1 0 0 0 0], テストラベルは：[1 0]
訓練データは：['1' '2' '4' '5' '6' '7' '8' '9'], テストデータは：['0' '3']
訓練ラベルは：[1 1 1 0 0 0 0 0], テストラベルは：[1 1]
訓練データは：['0' '2' '3' '4' '5' '6' '8' '9'], テストデータは：['1' '7']
訓練ラベルは：[1 1 1 1 0 0 0 0], テストラベルは：[1 0]
訓練データは：['0' '1' '2' '3' '4' '6' '7' '9'], テストデータは：['5' '8']
訓練ラベルは：[1 1 1 1 1 0 0 0], テストラベルは：[0 0]</pre>
出力を確認すると、テストデータの値が全て違うことがわかります。KFold()では全データが一度はテストデータとして扱われます。分割自体はランダムですので、目的変数の割合は各foldでバラバラです。</p>
<h4>シャッフル分割交差検証（Shuffle-Split Cross-Validation）</h4>
<p>シャッフル分割交差検証（以下Shuffle-Split）はK-Foldに少し改良が加えられた交差検証です。Shuffle-Splitの特徴は訓練・テストデータ両方の割合を指定できるところにあります。これによりデータセット全体からサンプルしたデータで交差検証を行うことができます。訓練・テストデータ両方に使用されていないデータが存在したり、逆に同じデータが2回テストデータに使用される場合もあります。データセットのサイズがとても大きい場合などに有効です。図10はShuffle-Splitを示しています。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/08/画像10.png" alt="" /></p>
<p>実装はscikit-learnに用意されているShuffleSplit()を使用します。ShuffleSplit()は分割数を引数に指定することができます。for文を使用すること、split()メソッドを使用することはKFold()と同様です。今回は5分割で実装します。また、train_sizeを0.7、test_sizeを0.2として、データセットの一部を使用していません。重要な引数に関しては以下に記載します。詳細については公式ドキュメントを参照してください。（参考：<a href="https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.ShuffleSplit.html" target="_blank" rel="noopener">scikit-leanr公式：ShuffleSplit</a>）</p>
<ul>
<li>n_splits：分割数</li>
<li>train_size：訓練データの割合</li>
<li>test_size：テストデータの割合</li>
<li>random_state：シード値</li>
</ul>
<pre class="crayon-plain-tag">#[IN]:

#ShuffleSplitのインポート
from sklearn.model_selection import ShuffleSplit

#ShuffleSplitの設定
ss = ShuffleSplit(n_splits = 5, train_size = 0.7, test_size = 0.2, random_state = 0)

#交差検証
for train, test in ss.split(df):
  print(f"訓練データは：{data[train].values}, テストデータは：{data[test].values}")
  print(f"訓練ラベルは：{label[train].values}, テストラベルは：{label[test].values}")</pre>
<pre class="crayon-plain-tag">#[OUT]

訓練データは：['4' '9' '1' '6' '7' '3' '0'], テストデータは：['2' '8']
訓練ラベルは：[1 0 1 0 0 1 1], テストラベルは：[1 0]
訓練データは：['1' '2' '9' '8' '0' '6' '7'], テストデータは：['3' '5']
訓練ラベルは：[1 1 0 0 1 0 0], テストラベルは：[1 0]
訓練データは：['8' '4' '5' '1' '0' '6' '9'], テストデータは：['2' '3']
訓練ラベルは：[0 1 0 1 1 0 0], テストラベルは：[1 1]
訓練データは：['9' '2' '7' '5' '8' '0' '3'], テストデータは：['6' '1']
訓練ラベルは：[0 1 0 0 0 1 1], テストラベルは：[0 1]
訓練データは：['7' '4' '1' '0' '6' '8' '9'], テストデータは：['5' '2']
訓練ラベルは：[0 1 1 1 0 0 0], テストラベルは：[0 1]</pre>
出力を確認すると、テストデータに同じ値（2や3）が含まれていることがわかります。またデータ全体が9個しかないも確認できます。このようにデータセットから抽出しながら交差検証を行えるのがShuffle-Splitの特徴です。また、この特徴はscikit-learnの他の交差検証でも実装されています。本稿ではShuffle-Splitのみ扱いましたが、他の組み合わせも同様です。</p>
<ul>
<li><a href="https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.StratifiedKFold.html" target="_blank" rel="noopener">StratifiedKFold</a>＋<a href="https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.ShuffleSplit.html" target="_blank" rel="noopener">ShuffleSplit</a>＝<a href="https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.StratifiedShuffleSplit.html" target="_blank" rel="noopener">StratifiedShuffleSplit</a></li>
<li><a href="https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GroupKFold.html" target="_blank" rel="noopener">GroupKFold</a>＋<a href="https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.ShuffleSplit.html" target="_blank" rel="noopener">ShuffleSplit</a>＝<a href="https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GroupShuffleSplit.html" target="_blank" rel="noopener">GroupShuffleSplit</a></li>
</ul>
<h4>層化K分割交差検証（Stratified K-Fold）</h4>
<p>層化K分割交差検証（以下：Stratified K-fold）は目的変数の割合が等しくなるように分割する交差検証です。データセットの目的変数は常に同じ割合であるとは限りません。目的変数の一つが極端に少ない場合、テストデータに目的変数の全種類が存在しない可能性が考えられます。Stratified K-foldはそのような問題を解決します。図11はStratified K-foldを示しています。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/08/画像11.png" alt="" /></p>
<p>目的変数の割合が5:5である場合を考えます。Stratified K-foldを使用すると訓練・テストデータ共に目的変数の割合が必ず5:5になります。目的変数の割合が9:1であっても同様です。これによって常に訓練とテストの条件を同一にすることができます。筆者も他に制約が存在しない場合はStratified K-foldを使用することが多いです。</p>
<p>実装はscikit-learnに用意されているStratifiedKFold()を使用しします。StratifiedKFold()は分割数を引数に指定することができます。for文を使用すること、split()メソッドを使用することはKFlod()と同様です。StratifiedKFold()ではsplitメソッド使用時に第二引数に目的変数を与えることで、訓練・テストに割り振ってくれます。今回は５分割で実装します。重要な引数に関しては以下に記載します。詳細については公式ドキュメントを参照してください。（参考：<a href="https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.StratifiedKFold.html" target="_blank" rel="noopener">scikit-leanr公式：StratifiedKFold</a>）</p>
<ul>
<li>n_splits：分割数</li>
<li>shuffle：シャッフルの有無</li>
<li>random_state：シード値</li>
</ul>
<pre class="crayon-plain-tag">#[IN]:

#StratifiedKFold
from sklearn.model_selection import StratifiedKFold

#StratifiedKFoldの設定
skf = StratifiedKFold(n_splits = 5, shuffle = True, random_state = 0)

#交差検証
for train, test in skf.split(df, label):
  print(f"訓練データは：{data[train].values}, テストデータは：{data[test].values}")
  print(f"訓練ラベルは：{label[train].values}, テストラベルは：{label[test].values}")</pre>
<pre class="crayon-plain-tag">#[OUT]

訓練データは：['0' '2' '3' '4' '6' '7' '8' '9'], テストデータは：['1' '5']
訓練ラベルは：[1 1 1 1 0 0 0 0], テストラベルは：[1 0]
訓練データは：['0' '1' '3' '4' '5' '6' '8' '9'], テストデータは：['2' '7']
訓練ラベルは：[1 1 1 1 0 0 0 0], テストラベルは：[1 0]
訓練データは：['1' '2' '3' '4' '5' '7' '8' '9'], テストデータは：['0' '6']
訓練ラベルは：[1 1 1 1 0 0 0 0], テストラベルは：[1 0]
訓練データは：['0' '1' '2' '4' '5' '6' '7' '8'], テストデータは：['3' '9']
訓練ラベルは：[1 1 1 1 0 0 0 0], テストラベルは：[1 0]
訓練データは：['0' '1' '2' '3' '5' '6' '7' '9'], テストデータは：['4' '8']
訓練ラベルは：[1 1 1 1 0 0 0 0], テストラベルは：[1 0]</pre>
出力を確認すると目的変数の1と0が同じ割合でテストデータに含まれていることがわかります。気になる方は引数のrandom_stateの値を変更してみてください。同様の結果が得られます。先ほども記載しましたが、StratifiedShuffleSplit()を使用するとShuffleSplit()の時と同様の効果を得られます。実装に関してはほとんど変わりませんので、興味がある方は調べてみてください。（参考：<a href="https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.StratifiedShuffleSplit.html" target="_blank" rel="noopener">scikit-learn公式：StratifiedShuffleSplit</a>）</p>
<h4>グループK分割交差検証（Group K-Fold）</h4>
<p>グループK分割交差検証（以下：Group K-Fold）は訓練・テストデータの両方に同一のグループが含まれることを避ける交差検証です。例えば医療画像などで、同一の患者が訓練・テストデータの間で被らないようにする場合などで使用されます。仮に、同じ患者のデータは他の患者に比べて似る傾向があるとします。訓練データで学習した患者と同一患者をテストデータで予測する場合、他の患者を予測するよりも簡単である可能性があります。図12は医療データの分割の例を示しています。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/08/画像12.png" alt="" /><br />
図12を見ると上図は同一患者が訓練とテストに存在しています。例えば病気の陽陰性を判断するモデルを作成する場合、上図では非常に簡単です。なぜなら訓練・テストデータに存在する同一患者は同じ診断結果である可能性が非常に高いためです。これでは正しく評価を行えません。この場合は下図のように、患者を分け、新たな患者が与えられたときに正しく診断できる予測モデルを作成することが求められます。図13はGroup K-Foldを示しています。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/09/画像13.png" alt="" /></p>
<p>図13のアルファベットはグループを表しています。訓練データとテストデータに同じグループが存在しないように分割されます。注意点としてはGroup K-Foldでは各グループが一度はテストデータに含まれることになります。つまり交差検証の回数Kはグループ数以下でなければならない点に注意てしてください。また、グループ毎の数が異なる場合、訓練とテストデータの割合はグループによって変化する場合があります。</p>
<p>実装はscikit-learnに用意されているGroupKFold()を使用しします。GroupKFold()は分割数を引数に指定することができます。for文を使用すること、split()メソッドを使用することはKFlod()と同様です。GroupKFold()ではsplitメソッド使用時にgroups引数にグループの変数を与えることで、訓練・テストに割り振ってくれます。今回は2分割で実装します。重要な引数に関しては以下に記載します。詳細については公式ドキュメントを参照してください。（参考：<a href="https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GroupKFold.html" target="_blank" rel="noopener">scikit-leanr公式：GroupKFold</a>）</p>
<ul>
<li>n_splits：分割するKの値</li>
</ul>
<pre class="crayon-plain-tag">#[IN]:

#GroupKFoldのインポート
from sklearn.model_selection import GroupKFold

#GroupKFoldの設定
gkf = GroupKFold(n_splits = 2)

#交差検証
for train, test in gkf.split(df,groups=group):
  print(f"訓練データは：{data[train].values}, テストデータは：{data[test].values}")
  print(f"訓練ラベルは：{label[train].values}, テストラベルは：{label[test].values}")
  print(f"訓練グループは：{group[train].values}, テストグループは：{group[test].values}")</pre>
<pre class="crayon-plain-tag">#[OUT]

訓練データは：['1' '3' '5' '7' '9'], テストデータは：['0' '2' '4' '6' '8']
訓練ラベルは：[1 1 0 0 0], テストラベルは：[1 1 1 0 0]
訓練グループは：[0 0 0 0 0], テストグループは：[1 1 1 1 1]
訓練データは：['0' '2' '4' '6' '8'], テストデータは：['1' '3' '5' '7' '9']
訓練ラベルは：[1 1 1 0 0], テストラベルは：[1 1 0 0 0]
訓練グループは：[1 1 1 1 1], テストグループは：[0 0 0 0 0]</pre>
出力を確認すると訓練・テストデータに同一のグループが存在していないことがわかると思います。GroupKFold()は引数にシャッフルの有無とシード値が存在しません。これにより人によっては使いづらさを感じることがあるかもしれません。その場合、GroupShuffleSplit()を使用することをお勧めします。同じグループが何度もテストデータに使用される場合はありますが、シード値の固定を行えるので再現性は確保しやすいと言えます。（参考：<a href="https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.GroupShuffleSplit.html" target="_blank" rel="noopener">scikit-learn GroupShuffleSplit</a>）</p>
<p>Stratified K-FoldとGroup K-Foldを合わせたStratifiedGroup KFoldも存在します。StratifiedGroup KFoldはその名の通り2つの機能を合わせた機能を有しています。使い方は他と同様に目的変数とグループの変数を引数に渡してあげるだけです。非常に便利ですので是非使ってみてください。（参考：<a href="https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.StratifiedGroupKFold.html" target="_blank" rel="noopener">scikit-learn StratifiedGroupKFold</a>）</p>
<h4>時系列交差検証（Time Series Split）</h4>
<p>時系列交差検証は時系列に沿った交差検証を行うための方法です。時系列データには1つのデータに対する過去と未来が存在します。仮に、未来のデータを用いて訓練を行なった場合、他の未来データに対する予測も容易になる可能性があります。そのため、時系列に沿った分割を行う必要性があります。時系列交差検証はこれらの問題を解決します。図14は時系列交差検証を示しています。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/08/画像14.png" alt="" /><br />
今までとは交差検証の形式が異なります。時間軸に沿ったようにデータ分割が行われ、テストデータ以降のデータが訓練データに使用されることはありません。また、テストデータの時間が後になるについれて訓練データに使用できるデータ量も多くなります。場合によっては訓練データの量を一定に揃える場合もあります。</p>
<p>実装はscikit-learnに用意されているTimeSeriesSplit()を使用します。TimeSeriesSplit()は分割数を引数に指定することができます。for文を使用すること、split()メソッドを使用することはKFlod()と同様です。TimeSeriesSplit()ではmax_train_sizeを指定することで訓練データのサイズを指定できます。今回3分割で実装します。重要な引数に関しては以下に記載します。詳細については公式ドキュメントを参照してください。（参考：<a href="https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.TimeSeriesSplit.html" target="_blank" rel="noopener">scikit-leanr公式：TimeSeriesSplit</a>）</p>
<ul>
<li>n_splits：分割数</li>
<li>max_train_size：訓練データのサイズ</li>
</ul>
<pre class="crayon-plain-tag">#[IN]:

#TimeSeriesSplitのインポート
#訓練データサイズの指定なし
from sklearn.model_selection import TimeSeriesSplit

#TimeSeriesSplitの設定
#訓練データサイズの指定なし
tss_a = TimeSeriesSplit(n_splits = 3, max_train_size = None)

#交差検証
for train, test in tss_a.split(data):
  print(f"訓練用データは：{np.sort(data[train])}, 検証用データは：{np.sort(data[test])}")</pre>
<pre class="crayon-plain-tag">#[OUT]

訓練用データは：['0' '1' '2' '3'], 検証用データは：['4' '5']
訓練用データは：['0' '1' '2' '3' '4' '5'], 検証用データは：['6' '7']
訓練用データは：['0' '1' '2' '3' '4' '5' '6' '7'], 検証用データは：['8' '9']</pre>
<pre class="crayon-plain-tag">#[IN]:

#TimeSeriesSplitの設定
#訓練データサイズの指定あり
tss_b = TimeSeriesSplit(n_splits = 3, max_train_size = 2)

#交差検証
for train, test in tss_b.split(data):
  print(f"訓練用データは：{np.sort(data[train])}, 検証用データは：{np.sort(data[test])}")</pre>
<pre class="crayon-plain-tag">#[OUT]

訓練用データは：['2' '3'], 検証用データは：['4' '5']
訓練用データは：['4' '5'], 検証用データは：['6' '7']
訓練用データは：['6' '7'], 検証用データは：['8' '9']</pre>
出力を確認すると図14のように時系列に沿って交差検証が行われていることが分かるかと思います。注意点としてはTimeSeriesSplit()はデータの並び順を時系列として扱う点です。そのため、交差検証の実装前にはデータを時系列順に並び替えておく必要があります。</p>
<p>ここまで、複数の交差検証を解説してきました。データ分割の話も含んでいるので難しい部分もあるかと思います。言葉では理解しきれなかったという方は後半の実装まで読んでいただき、もう一度読み返して見ることをお勧めします。</p>
<h3>モデルの汎化性能を評価する交差検証の実装</h3>
<p>ここからは以下の3つをコードとともに解説します。1つ1つ丁寧に解説していきますので初学者の方も是非取り組んでください。</p>
<ul>
<li>モデルの汎化性能を評価する交差検証の実装</li>
<li>ハイパーパラメータチューニングのための交差検証の実装</li>
<li>コンペティションで使用される交差検証の実装</li>
</ul>
<p>本節では、モデルの汎化性能を評価するための交差検証を行います。先ほどの解説でもあったようにこの交差検証は、研究などのデータセットが1つの場面で比較的使用されます。最も基礎的な実装になりますので、確実に押さえてください。前節までの実装は引き継ぎません。改めてライブラリのインポートから始めます。ポイントとなるライブラリについてはその都度追加していきます。</p><pre class="crayon-plain-tag">#[IN]

#ライブラリのインポート
import numpy as np
import pandas as pd
import warnings
warnings.filterwarnings('ignore')</pre><p>
次に、データセットを準備します。今回使用するデータセットは乳がんのデータセットです。30の特徴量から悪性と良性の2値分類を行います。データ数は全部で569です。本稿では各特徴量に関する解説は省きますが、興味のある方はダウンロード元を調べてみてください。（参考：<a href="https://archive.ics.uci.edu/ml/datasets/Breast+Cancer+Wisconsin+(Diagnostic)" target="_blank" rel="noopener">Breast Cancer Wisconsin (Diagnostic) Data Set</a>）</p><pre class="crayon-plain-tag">#[IN]

#データセットの追加
from sklearn.datasets import load_breast_cancer
src = load_breast_cancer()

#データセットをDataFrameにする
data = pd.DataFrame(src.data, columns = src.feature_names)
label = pd.DataFrame(src.target, columns = ["Target"])
data</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/08/スクリーンショット-2021-08-30-16.32.18.png" alt="" /></p>
<h4>ホールドアウト法</h4>
<p>次に、データ分割に移ります。今回はホールドアウト法を用いた2分割と、交差検証を用いた結果を比較したいと思います。先にホールドアウト法から行います。ホールドアウト法はtrain_test_split()を用いて行います。8割を訓練データ、2割をテストデータにします。</p><pre class="crayon-plain-tag">#[IN]:

#train_test_splitのインポート
from sklearn.model_selection import train_test_split

#データ分割
train_x, test_x, train_y, test_y = train_test_split(data, label ,test_size = 0.2, shuffle = True, random_state = 0)</pre><p>
次に、データの標準化を行います。標準化は学習の効率を高めるために使用します。テストデータは訓練データの値を用いて標準化されます。標準化は交差検証の際にもう一度登場しますので、知識に不安のある方は以下を参考にしてください。（参考：<a href="https://www.codexa.net/normalization-python/" target="_blank" rel="noopener">正規化・標準化を徹底解説</a>）</p><pre class="crayon-plain-tag">#[IN]:

#StandardScalerのインポート
from sklearn.preprocessing import StandardScaler

#標準化のクラスを定義
ss_holdout = StandardScaler()

#訓練データを標準化
train_x = ss_holdout.fit_transform(train_x)
#訓練データの値を用いてテストデータを標準化
test_x = ss_holdout.transform(test_x)</pre><p>
次に、モデル構築を行います。使用するのはロジスティック回帰です。8割の訓練データを用いて学習を行います。ロジスティック回帰は機械学習の中で最も初歩的な手法の1つです。知識的に不安のある方は以下のコースの受講をおすすめします。（参考：<a href="https://www.codexa.net/tutorial-logistic-regression-for-beginner/" target="_blank" rel="noopener">機械学習チュートリアル：ロジスティック回帰</a>）</p><pre class="crayon-plain-tag">#[IN]

#ロジスティック回帰のインポート
from sklearn.linear_model import LogisticRegression

#ロジスティック回帰の設定
lr = LogisticRegression(solver = "sag", random_state = 0)
#訓練
lr.fit(train_x, train_y)
#テストデータに対する予測
pred  = lr.predict(test_x)
pred</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]

array([0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0,
       1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1,
       1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1,
       0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1,
       0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1,
       1, 0, 0, 1])</pre><p>
次に、予測した値の評価を行います。評価に関しては正解率（Accuracy）を用います。評価指標は機械学習を学ぶ上で非常に重要です。評価指標に対してより詳しく知りたい方は以下の記事を確認してください。（参考：<a href="https://www.codexa.net/ml-evaluation-cls/" target="_blank" rel="noopener">機械学習の評価指標 分類編</a>）</p><pre class="crayon-plain-tag">#[IN]

#Accuracyを計算
from sklearn.metrics import accuracy_score
accuracy_score(test_y, pred)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]

0.9649122807017544</pre><p>
正解率が出力されました。しかし、この正解率にはどれほどの信頼が置けるでしょうか。0.96という結果は非常に高いようにも感じられますが、それはホールドアウト法で偶然にもそういった結果が得られたかもしれません。次の交差検証で検証します。</p>
<h4>層化K分割交差検証（Stratified K-Fold）</h4>
<p>交差検証に入る前に準備を行います。先ほど行なった標準化を交差検証でも行います。交差検証の場合、fold毎に標準化を適用し直さなければなりません。交差検証時の標準化の適用を図15に示します。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/08/画像15.png" alt="" /></p>
<p>図15のように標準化を適用するために、Pipelineを用います。Pipelineはデータの前処理から学習までを一連の処理として定義できます。今回は標準化とロジスティック回帰の構築を一連の処理としてまとめます。これにより、交差検証を実装する際にも、fold毎に標準化を行うことができます。（scikit-lean公式：<a href="https://scikit-learn.org/stable/modules/generated/sklearn.pipeline.Pipeline.html" target="_blank" rel="noopener">Pipeline</a>）</p><pre class="crayon-plain-tag">#[IN]:

#StratifiedKFold
from sklearn.pipeline import Pipeline

#標準化の設定
ss_kfold = StandardScaler()

#パイプラインの設定
pl = Pipeline([('tf', ss_kfold), ('lr', lr)])</pre><p>
次に、交差検証を行います。本節で使用する交差検証はStratified K-Foldになります。使用の理由としては今回の目的変数の割合が5:5でないためです。悪性：良性＝212：357であるため、目的変数の割合に若干の違いがあります。そのため、Stratified K-Foldを用いて、訓練とテストの目的変数の割合を等しくします。</p>
<p>実装は非常に簡単です。前半での説明は読者の皆様に直感的に理解いただけるようfor文とsplit()などを用いて実装しました。このやり方は非常に便利ですが、少し難易度が高いです。評価を出すことだけが目的であれば、cross_val_score()を用いた方が簡単です。主な引数に関する情報は以下に示します。</p>
<ul>
<li>第１引数：estimator（モデルなど）</li>
<li>第２引数：特徴量</li>
<li>第３引数：目的変数</li>
<li>group：GruoKfoldで指定する特徴量</li>
<li>scoreing：評価指標</li>
<li>cv：交差検証の指定</li>
</ul>
<p>引数のscoreingですが、様々な評価指標を設定できます。他の評価指標に関しては以下を参考にしてください。また、本稿で紹介する引数は上記6つですが、cross_val_score()には他にも引数が存在します。上記6つも用途によって若干条件が変更される場合があります。より詳細に知りたい方は公式ドキュメントを参考にしてください。（参照：<a href="https://scikit-learn.org/stable/modules/model_evaluation.html" target="_blank" rel="noopener">scikit-learn公式：scoreing</a>・<a href="https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.cross_val_score.html" target="_blank" rel="noopener">cross_val_score</a>）</p><pre class="crayon-plain-tag">#[IN]:

#StratifiedKFold
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import cross_val_score

#StratifiedKFoldの設定
skf = StratifiedKFold(n_splits = 5, shuffle = True, random_state = 0)

#指定したモデル+交差検証で評価を行える
#scoreには分割の数だけ評価指標がリストで入る
score = cross_val_score(pl, data, label, scoring = "accuracy", cv = skf)

#各foldでの正解率
print(score)
#平均の正解率
print(score.mean())</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]

[0.95614035 0.98245614 0.98245614 1.         0.98230088]
0.98067070330694</pre><p>
表示された正解率はホールドアウト法のときよりも上がっています。もしかするとホールドアウト法の時は偶然予測が難しかったのかもしれません。このように、交差検証を行うとより正確な評価を行うことができます。本節は、この後の実装にも関連する部分が多いです。理解しておいてください。</p>
<h3>ハイパーパラメータチューニングのための交差検証の実装</h3>
<p>本節では、ハイパーパラメータチューニングに用いられる交差検証の実装を行います。汎化性能を評価する場合との違いを意識しながら進めるようにしてください。先でも述べたように本稿ではハイパーパラメータに関する基本的な解説は省いています。あくまで交差検証のイメージを掴むための実装であることをご了承ください。</p>
<p>データセットは先ほどと同様です。もう一度ライブラリのインポートから始めますが、2回目ですのである程度はまとめています。</p><pre class="crayon-plain-tag">#[IN]

#ライブラリのインポート
import numpy as np
import pandas as pd
import warnings
warnings.filterwarnings('ignore')
from sklearn.datasets import load_breast_cancer

#データセットの追加
src = load_breast_cancer()

#データセットをDataFrameにする
data = pd.DataFrame(src.data, columns=src.feature_names)
label = pd.DataFrame(src.target, columns=["label"])</pre><p>
今回もホールドアウト法を用いて訓練データとテストデータに分割します。ハイパーパラメータチューニングの後、最終的な評価に使用するためです。先ほどとは違い、引数のstratifyを追加しています。これはStratified K-Foldと同様でデータ分割時にラベルの割合を等しくすることができます。便利な引数ですので、是非覚えていてください。</p><pre class="crayon-plain-tag">#[IN]:

#train_test_splitのインポート
from sklearn.model_selection import train_test_split

#データ分割
train_x, test_x, train_y, test_y = train_test_split(data, label , test_size = 0.2, shuffle = True, stratify = label, random_state = 0)</pre><p>
次に、データの前処理とモデル構築を組み合わせてPipelineを作成していきます。モデルは非線形SVM（サポートベクターマシン）を用います。サポートベクターマシンも機械学習を扱う上では重要なモデルの1つです。興味のある方には以下のコースをおすすめします。（参考：<a href="https://www.codexa.net/support-vector-machine-tutorial/" target="_blank" rel="noopener">機械学習 チュートリアル サポートベクターマシン</a>）</p><pre class="crayon-plain-tag">#[IN]:

#StandardScalerのインポート
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.pipeline import Pipeline

# 標準化のクラスを定義
ss = StandardScaler()

#デフォルトの状態でモデル構築
svc = SVC()

#Pipelineの作成
pl = Pipeline([("tf", ss), ("svc", svc)])</pre><p>
次に、ハイパーパラメータチューニングを行います。今回使用する手法はグリッドサーチです。「全通り試す」と思っていただければ問題ありません。難しく見えるかもしれませんが実装はシンプルです。まず試行したいハイパーパラメータを準備します。これらは辞書型で用意すれば問題ありません。今回、SVCにおけるlinearにはgmmaは利用できません。そのため、linearだけは別で用意しています。</p>
<p>今回のハイパーパラメータはPipeline上のモデルに適用することになります。この場合、Pipeline上のどのステップに適用するかを指定する必要があります。そのため、通常とはパラメータの名前が若干変わります。<br />
<code>Pipelineでのステップ名__パラメータ名：[試行するパラメータ]</code><br />
のように、Pipelineでのステップ名が必要になります。</p><pre class="crayon-plain-tag">#[IN]:

#必要なライブラリのインポート
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import GridSearchCV

#試行するハイパーパラメータ
parameters = [
              {"svc__kernel": ["rbf", "sigmoid", "poly"],
               "svc__gamma": [0.001, 0.0001],
               "svc__C": [1, 10, 100, 1000],
               "svc__random_state": [0]},
              {"svc__kernel": ["linear"],
               "svc__C": [1, 10, 100, 1000],
               "svc__random_state": [0]}]

#グリッドサーチ内で行う交差検証の種類を指定
skf = StratifiedKFold(n_splits = 5, shuffle = True, random_state = 0)

#グリッドサーチの設定
clf = GridSearchCV(pl, parameters, cv = skf, scoring = "accuracy")
#交差検証しながらグリッドサーチ開始
clf.fit(train_x, train_y)

#交差検証の中で最も良かったスコアを表示
print(clf.best_score_)
#その時のパラメータを表示
print(clf.best_params_)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]

0.9824175824175825
{'svc__C': 1000, 'svc__gamma': 0.0001, 'svc__kernel': 'rbf', 'svc__random_state': 0}</pre><p>
表示されているのは検証データに対して最も高い正解率と、その時のハイパーパラメータです。これで、交差検証によるハイパーパラメータの探索は終了です。最後にベストなハイパーパラメータを用いたモデルを使用して、テストデータの正解率を求めます。</p><pre class="crayon-plain-tag">#[IN]

#accuracy_scoreのインポート
from sklearn.metrics import accuracy_score

#チューニング後のモデル
best_svc = clf.best_estimator_
#テストデータに対する予測
pred = best_svc.predict(test_x)
#正解率の表示
accuracy_score(test_y, pred)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]

0.9736842105263158</pre><p>
テストデータの正解率は交差検証時の正解率よりも少し下がっています。この理由はハイパーパラメータが検証データに対して最適化されているためであると考えられます。それでも0.97と非常に高い正解率で識別できています。他にも交差検証やチューニング方法を変更すれば違う結果が得られると思います。読者の皆様も様々な方法で本節のコードを改造してみてください。</p>
<h3>コンペティションで使用される交差検証の実装</h3>
<p>最後にコンペティションで使用される交差検証の実装を行います。今までとは違い、最初からtrain.csvとtest.csvに分かれているような状況を想定します。基本的な流れは同じですが、モデルを保存するという点が他とは異なります。kaggleなどのコンペティションに参加してみたい方は是非参考にしてください。</p>
<p>まずはデータセットを準備します。今回使用するデータセットはkaggleのチュートリアルで使用されるタイタニックのデータセットです。こちらのページからデータセットをダウンロードしていただき、Google Colab左の「ファイル」にアップロードしてください。まずはライブラリのインポートを行います。本節では必要なライブラリは全て先にインポートしています。</p><pre class="crayon-plain-tag">#[IN]

#ライブラリのインポート
import pandas as pd
import numpy as np
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import StratifiedKFold
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score</pre><p>
次に、データのロードを行います。各特徴量についての解説は以下の通りです。</p>
<ul>
<li>PassengerId – 乗客識別ユニークID</li>
<li>Survived – 生存フラグ（0=死亡、1=生存）</li>
<li>Pclass – チケットクラス</li>
<li>Name – 乗客の名前</li>
<li>Sex – 性別（male=男性、female＝女性）</li>
<li>Age – 年齢</li>
<li>SibSp – タイタニックに同乗している兄弟/配偶者の数</li>
<li>parch – タイタニックに同乗している親/子供の数</li>
<li>ticket – チケット番号</li>
<li>fare – 料金</li>
<li>cabin – 客室番号</li>
<li>Embarked – 出港地（タイタニックへ乗った港）</li>
</ul>
<pre class="crayon-plain-tag">#[IN]

#データのロード
_train=pd.read_csv("train.csv")
_test=pd.read_csv("test.csv")

#データの結合
all = pd.concat([_train,_test])
all</pre>
<img src="https://www.codexa.net/wp-content/uploads/2021/08/スクリーンショット-2021-08-30-16.37.09.png" alt="" /></p>
<p>次にデータの前処理を行います。前処理の詳細な解説は省きますが、行なっているのは基本的な欠損値の補完です。文字列に関してはLabelEncoder()を使用して、数値に変換しています。また、前処理に使用可能なデータ数を増やすため、訓練データとテストデータを結合しています。これはコンペティションでのテクニックの1つです。しかし、リークを引き起こす可能性もあるので注意してください。</p><pre class="crayon-plain-tag">#[IN]

#欠損値の補完
all["Age"] = all["Age"].fillna(all["Age"].mean())
all["Fare"] = all["Fare"].fillna(all["Fare"].mean())
all["Embarked"] = all["Embarked"].fillna("S")

#文字列を数値に変換
colum=["Sex","Ticket","Embarked"]
for c in colum:
  le = LabelEncoder()
  le.fit(all[c])
  all[c] = le.fit_transform(all[c])
all</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/08/スクリーンショット-2021-08-30-16.36.11.png" alt="" /></p>
<p>前処理が完了しました。結合していたデータを訓練データとテストデータに戻します。</p><pre class="crayon-plain-tag">#[IN]

#訓練データとテストデータに戻す
train = all[:len(_train)]
test = all[len(_train):]</pre><p>
次に、モデル構築に使用する特徴量を選択します。今回は、「Pclass」「Sex」「Age」「SibSp」「Parch」「Ticket」「Fare」「Embarked」です。また目的変数としてSurvivedも同様に切り取っておきます。</p><pre class="crayon-plain-tag">#[IN]

#訓練に使用する特徴量
#DataFrameのままだと後ほど不便なため、valuesを使用してnumpyに変換
data = train[["Pclass","Sex","Age","SibSp","Parch","Ticket","Fare","Embarked"]].values
label = train["Survived"]</pre><p>
次に、交差検証を行います。前節までと同様に、fold数5でStratifiedKFold()を用います。これまではcross_val_sore()を用いてきましたが、本節で最初に紹介したfor文とsplit()を用いて実装します。こちらの書き方も知っておくと便利ですので、1行1行確認してください。モデルにはランダムフォレストを使用します。（参考：<a href="https://www.codexa.net/decision-tree-random-forest/" target="_blank" rel="noopener">機械学習 チュートリアル 決定木とランダムフォレスト</a>）</p><pre class="crayon-plain-tag">#[IN]:

#StratifiedKFold
from sklearn.model_selection import StratifiedKFold

#StratifiedKFoldの設定
skf = StratifiedKFold(n_splits = 5, shuffle = True, random_state = 0)

#モデル保存用のリスト
model_list=[]

#交差検証
for train, val in skf.split(data, label):
  #訓練データと検証データに分割
  train_x = data[train]
  val_x = data[val]
  train_y = label[train]
  val_y = label[val]
  
  #ランダムフォレストの定義
  tree = RandomForestClassifier(random_state = 0)
  #学習
  tree.fit(train_x, train_y)
  #予測
  pred=tree.predict(val_x)
  #検証データに対する正解率を求める
  print(accuracy_score(val_y, pred))
  #モデルを配列に保存
  model_list.append(tree)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]

0.8435754189944135
0.848314606741573
0.848314606741573
0.8033707865168539
0.8426966292134831</pre><p>
検証用データに対する予測結果が5回出力されています。どれも0.84前後です。最後に、各foldで作成したモデルを使用して多数決による投票を行います。テストデータから訓練データと同じ特徴量を取得します。</p><pre class="crayon-plain-tag">#[IN]:

#予測に使用する特徴量
test_data = test[["Pclass","Sex","Age","SibSp","Parch","Ticket","Fare","Embarked"]].values</pre><p>
次に、テストデータを各foldで作成したモデルで予測します。各foldモデルの予測は、辞書型に保存します。最後に、辞書からDataFrameに変換を行うことで、各行がそれぞれのモデルが予測した値になっています。</p><pre class="crayon-plain-tag">#[IN]:

#結果を辞書に保存
solution = {}

#各モデルで予測
for i, model in enumerate(model_list):
  test_pred = model.predict(test_data)
  solution[str(i)+"_model"] = test_pred

#辞書からDataFrameに変更
solution=pd.DataFrame(solution)
solution</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/08/スクリーンショット-2021-08-30-16.36.27.png" alt="" /><br />
次に、多数決を取ります。DataFrameのmode()を使用することで、最頻値を取得することができます。デフォルトでは各列での適用ですので、axis=1に設定し、各行での適用に変更します。アンサンブルに関しては以下を参考にしてください。（参考：<a href="https://www.codexa.net/what-is-ensemble-learning/" target="_blank" rel="noopener">機械学習上級者は皆使ってる？！アンサンブル学習の仕組みと3つの種類について解説します</a>）</p><pre class="crayon-plain-tag">#[IN]

#最頻値を取得
solution_max = solution.mode(axis = 1).values</pre><p>
最後に、サブミットするためのcsvファイルに書き出します。「ファイル」にsubmission.csvが保存されているはずです。</p><pre class="crayon-plain-tag">#[IN]

# PassengerIdを取得
PassengerId = np.array(test["PassengerId"]).astype(int)
 
# my_prediction(予測データ）とPassengerIdをデータフレームへ落とし込む
my_solution = pd.DataFrame(solution_max.astype(int), index=PassengerId, columns = ["Survived"])
 
# my_tree_one.csvとして書き出し
my_solution.to_csv("submission.csv", index_label = ["PassengerId"])</pre><p>
submission.csvを<a href="https://www.kaggle.com/c/titanic/submit" target="_blank" rel="noopener">こちら</a>からサブミットしてみましょう。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/08/スクリーンショット-2021-08-30-16.20.21.png" alt="" /><br />
正解率は0.77でした。他にも、交差検証の種類やモデルを変えて、サブミットのスコアが変化するか試してみてください。これでコンペティションで使用する交差検証の実装は完了になります。</p>
<h3>まとめ</h3>
<p>交差検証1つとっても様々な手法があり混乱された方もいるかもしれません。それだけ機械学習が難しいとも言えますが、筆者としてはそこに面白さがあるとも思います。是非、本稿を足場かけにして様々な実装にチャレンジしてみてください。</p>
<p>本稿を読んで、少しでも読者の皆様が機械学習に興味を持っていただけたら幸いです。</p>
<p>codexaでは機械学習初学者に向けたコースを複数提供しています。</p>
<ul>
<li><a href="https://www.codexa.net/numpy/" target="_blank" rel="noopener">Numpy 入門</a></li>
<li><a href="https://www.codexa.net/matplotlib/" target="_blank" rel="noopener">Matplotlib 入門</a></li>
<li><a href="https://www.codexa.net/pandas/" target="_blank" rel="noopener">Pandas 入門</a></li>
<li><a href="https://www.codexa.net/linear-basics/" target="_blank" rel="noopener">線形代数 入門</a></li>
<li><a href="https://www.codexa.net/statistics-for-machine-learning-first/" target="_blank" rel="noopener">統計入門（前編）</a></li>
<li><a href="https://www.codexa.net/statistics-for-machine-learning-second/" target="_blank" rel="noopener">統計入門（後編）</a></li>
<li><a href="https://www.codexa.net/linear-regression-for-beginner/" target="_blank" rel="noopener">線形回帰 入門</a></li>
</ul>
<p>また、すでに機械学習の基礎知識がある方に向けて、機械学習の様々な手法を詳しく解説したチュートリアルも公開しています。</p>
<ul>
<li><a href="https://www.codexa.net/tutorial-linear-regression-for-beginner/" target="_blank" rel="noopener">実践 線形回帰</a></li>
<li><a href="https://www.codexa.net/tutorial-logistic-regression-for-beginner/" target="_blank" rel="noopener">実践 ロジスティック回帰</a></li>
<li><a href="https://www.codexa.net/decision-tree-random-forest/" target="_blank" rel="noopener">決定木とランダムフォレスト</a></li>
<li><a href="https://www.codexa.net/support-vector-machine-tutorial/" target="_blank" rel="noopener">サポートベクターマシン</a></li>
<li><a href="https://www.codexa.net/naive-bayes-tutorial/" target="_blank" rel="noopener">ナイーブベイズ（単純ベイズ分類器）</a></li>
<li><a href="https://www.codexa.net/xgboost-tutorial/" target="_blank" rel="noopener">XGBoost</a></li>
<li><a href="https://www.codexa.net/keras-cnn-python/" target="_blank" rel="noopener">はじめての画像認識</a></li>
</ul>
<p>是非、皆様のご受講をお待ちしております。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.codexa.net/cross_validation/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Target Encodingとは？3種類のターゲットエンコーディングとPython実装方法を徹底解説</title>
		<link>https://www.codexa.net/target_encoding/</link>
					<comments>https://www.codexa.net/target_encoding/#respond</comments>
		
		<dc:creator><![CDATA[codexaチーム]]></dc:creator>
		<pubDate>Thu, 27 Jan 2022 02:00:16 +0000</pubDate>
				<category><![CDATA[機械学習]]></category>
		<guid isPermaLink="false">https://www.codexa.net/?p=3076</guid>

					<description><![CDATA[みなさんはデータ分析コンペティションに参加されたことはあるでしょうか。現在はkaggleやSIGNATEなど様々なコンペティションが開催されています。コンペティションにチャレンジされている方であれば、ほとんどの方がメダル [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>みなさんはデータ分析コンペティションに参加されたことはあるでしょうか。現在は<a href="https://www.codexa.net/what-is-kaggle/">kaggle</a>やSIGNATEなど様々なコンペティションが開催されています。コンペティションにチャレンジされている方であれば、ほとんどの方がメダルに憧れを抱いていると思います。筆者もその1人です。</p>
<p>データ分析の作業割合は前処理がほとんどを占めると言われています。コンペティションでも、データセットに対してどれだけ有効な特徴量を作成できるかが順位に直結します。しかし、初学者の方にとってはこのハードルは高いものがあります。その理由は、有効な特徴量がデータセットによって異なり、多くの特徴量は分析者の経験から作成される場合が多いからです。</p>
<p>本稿では、特徴量生成の中でも中級レベルに位置するTarget Encoding（読み：ターゲット・エンコーディング）について解説します。コンペティションの上級者も使用する手法ですので、機械学習の脱初心者を目指す方や、コンペティションで上位を目指す方は確実に押さえておきましょう。</p>
<h3>本編に入る前に</h3>
<p>本稿のTarget Encodingは「特徴量作成」の中の一手法でしかありません。そのため、コンペティション自体を知らない方や、特徴量という言葉に馴染みのない方は先に基本的な内容を押さえておくことをお勧めします。データの読み込みから評価までの基本的な流れ（ベースライン）に関しては、本稿では省略させていただきます。</p>
<ul>
<li><a href="https://www.codexa.net/kaggle-titanic-beginner/" target="_blank" rel="noopener">【Kaggle初心者入門編】タイタニック号で生き残るのは誰？</a></li>
<li><a href="https://www.codexa.net/kaggle-mercari-price-suggestion-challenge/" target="_blank" rel="noopener">Kaggle メルカリ価格予想チャレンジの初心者チュートリアル</a></li>
</ul>
<h3>Target Encodingとは</h3>
<p>Target Encoding（Target Mean Encoding）とはカテゴリカル（質的）データを数値に変換する方法の1つです。様々な手法があるのですが、Target Encodingの一番の特徴は<strong>目的変数を使用する</strong>という点です。筆者の言葉で誤解を恐れずに言うのであればTarget Encodingが生み出すのは「<strong>値が大きいほど目的変数の値も大きい確率が高い</strong>」特徴量ということになります。目的変数という答えを利用するTarget Encodingはデータセットによっては非常に強力な力を持ちます。</p>
<p>Target Encodingの変換を言葉にすると「目的変数の平均値を特徴量にする」です。<a href="https://www.codexa.net/python-outlier/">外れ値</a>などの状況によっては中央値などをとる場合もあるのですが、本稿では平均値を使用します。イメージとしては図1のようになります。本稿では可能な限り理解しやすいよう、0と1の2値分類を想定しています。（参考：<a href="https://www.codexa.net/python-outlier/" target="_blank" rel="nofollow noopener">外れ値とは？Pythonを使用して外れ値の検出方法を実装してみよう（全コード公開中）</a>）</p>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/06/図1-1.png" alt="" /><br />
Target Encodingの基本はこれだけです。図1の場合だと、特徴量Aは目的変数が1をとる確率が他に比べて高いということになります。ここまでは難しいと感じない方がほとんどだと思います。しかしTarget Encodingが難しいと言われる所以は、この「訓練データの目的変数の平均」をどのようにして取得するかという点にあります。取得方法については後述します。</p>
<h4>リークとは？</h4>
<p>Target Encodingを理解するために必須な「リーク」と呼ばれる事象について解説します。リーク（Leakage）とはデータ分析の界隈で比較的よく使用される言葉です。kaggleの公式では「リークとは、訓練データに予期しない追加情報が存在することで、モデルまたは機械学習アルゴリズムが現実的でない優れた予測を行うことを可能にすること」とあります。複数のタイプがあるのですが、Target Encodingでは訓練データにおいて目的変数の情報が漏れる可能性があります。（参考：<a href="https://www.kaggle.com/docs/competitions#leakage" target="_blank" rel="noopener">kaggle公式：What is Leakage</a>）</p>
<p>リークを起こすと訓練データに対しての予測は非常に高い評価になるのに対し、テストデータの評価が下がることがあります。これはモデルが訓練データに用意されている目的変数の情報から、強く影響を受けてしまったことによりモデルの汎用性が下がる場合に起こる現象です。言葉だと少し難しいと思いますので、詳細は実装も含めて後述します。</p>
<h4>テストデータへの適応</h4>
<p>訓練データには目的変数が存在しますが、テストデータには存在しません。そのため、テストデータのカテゴリカル変数は訓練データの値を用いてTarget Encodingを行うことになります。テストデータへの適応は非常にシンプルです。カテゴリカル変数ごとに訓練データの目的変数の平均をあたはめるだけで完了します。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/06/図2-1.png" alt="" /></p>
<h3>Target Encodingの種類</h3>
<p>先ほど少し触れましたが、Target Encodingには「目的変数の平均」の取り方によっていくつか種類があります。リークを起こしやすいものから起こしにくいものまで存在するので、しっかりと理解して扱えるようにする必要があります。本節では仮のデータセットを作成し、ある程度わかりやすい数値でTarget Encodingを実装していきます。解説では数値がどのように変化しているのか1つ1つ追っていきます。本稿で解説するのは以下の3つです。</p>
<ul>
<li>Greedy Target Encoding</li>
<li>Leave-one-out Target Encoding</li>
<li>Holdout Targer Encoding</li>
</ul>
<p>実装はGoogle Colabを用いて実装していきます。ライブラリをそのままインポートすれば同じように実装可能です。是非、ご自身でも実装してみてください。Google Colabを使用したことがない方は下記の記事を参考にしてください。（参考：<a href="https://www.codexa.net/how-to-use-google-colaboratory/" target="_blank" rel="noopener">Google Colabの知っておくべき使い方 – Google Colaboratoryのメリット・デメリットや基本操作のまとめ</a>）</p>
<h4>Greedy Target Enoding</h4>
<p>最初は、Greedy Target Encoding（Greedy Target Statistic）です。この方法は単純にデータセット全体から目的変数の平均を取得します。しかし、この方法はリークが起こる可能性があります。図2は特徴量の1つに焦点を当てた時のGreedy Target Encodingです。リークが起こる際の理由に関しては実装コードを確認した上で解説します。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/06/図3-1.png" alt="" /><br />
まずは仮のデータセットを用意します。pandsをインポートし、DataFrameでデータセットを作成します。仮のデータセットは10行×3列です。目的変数以外の2列に関しては文字列で作成します。このDataFrameを用いて3種類のTarget Encodingの実装を行います。</p><pre class="crayon-plain-tag">#[IN]:

#ライブラリのインポート
import pandas as pd
import warnings
warnings.filterwarnings('ignore')

#DataFrameで仮の訓練データセットを作成（10×3）
df = pd.DataFrame({
    "column1" : ["A","A","A","A","A","B","B","B","B","B"],
    "column2" : ["C","D","D","D","D","D","D","D","E","E"],
    "target" : [1, 0, 0, 1, 0, 0, 1, 1, 0, 1]
})

#先頭から10行を表示
df.head(10)</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-24-16.13.22.png" alt="" /><br />
仮のデータセットが作成できました。targetの列が今回の目的変数となります。column1とcolumn2は文字列ですので、数値に変換してあげる必要があります。そこで先ほど解説したGreedy Target Encodingを用いてcolumn1とcolumn2を数値に変換します。まずは各特徴量ごとの目的変数の平均をとります。column1であればAとB、column2であればCとDとEです。</p><pre class="crayon-plain-tag">#[IN]:

#各特徴量ごとの目的変数の平均をとる
col1_means = df.groupby("column1")["target"].mean()
col2_means = df.groupby("column2")["target"].mean()

print(col1_means)
print(col2_means)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

column1
A    0.4
B    0.6
Name: target, dtype: float64
column2
C    1.000000
D    0.428571
E    0.500000
Name: target, dtype: float64</pre><p>
各特徴量ごとの目的変数の平均を取得できました。これを特徴量として新たな列に追加します。本来であれば文字列自体を置き換えますが、本節では見やすいように新たな列を作成し、Greedy Target Encodingを適用した値を入れたいと思います。</p><pre class="crayon-plain-tag">#[IN]:

#目的変数の平均を特徴量として新たな列に加える
df["column1_target"] = df["column1"].map(col1_means)
df["column2_target"] = df["column2"].map(col2_means)
df</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-24-16.13.53.png" alt="" /><br />
Greedy Target Encodingが完了しました。簡単に実装できましたが、今回の実装ではリークが起こっています。1行目を見てください。目的変数（target）と特徴量（column2_target）の値が同じです。Greedy Target Encodingでは自分自身の目的変数も計算に加えてしまっているため、カテゴリカル変数の数が1つだと特徴量の値がそのまま目的変数になっています。これではテストデータが与えられた時に正しく評価することは厳しく、正しい特徴量生成とは言えません。場合によってはSmoothingなどのテクニックを使ってリークを起こしにくくすることもできます。Smoothingに関しては後述します。</p>
<h4>Leave-one-out Target Encoding</h4>
<p>次に、Leave-one-out Target Encoding（Leave-one-out Target Statistic）です。この方法は自身が持つ目的変数以外の目的変数の平均を取得します。この方法もリークが起こる可能性があるので注意が必要です。図3は特徴量の1つに焦点を当てた時のLeave-one-out Target Encodingです。こちらも、リークが起こる際の理由に関しては、実装コードを確認した上で解説します。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/06/図4.png" alt="" /><br />
Greedy Target Encodingと同様のデータセットを用意します。</p><pre class="crayon-plain-tag">#[IN]:

#ライブラリのインポート
import pandas as pd
import warnings
warnings.filterwarnings('ignore')

#DataFrameで仮のデータセットを作成（10×3）
df = pd.DataFrame({
    "column1" : ["A","A","A","A","A","B","B","B","B","B"],
    "column2" : ["C","D","D","D","D","D","D","D","E","E"],
    "target" : [1, 0, 0, 1, 0, 0, 1, 1, 0, 1]
})
#先頭から10行を表示
df.head(10)</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-24-16.14.03.png" alt="" /><br />
Greedy Target Encodingでは目的変数の平均を取得するだけで実装できましたが、Leave-one-out Target Encodingでは自身の値は含まないため、実装が少し複雑になります。まずは特徴毎のカウント数と目的変数の合計を取得します。</p><pre class="crayon-plain-tag">#[IN]:

#column1の特徴毎のカウント数を取得する
col1_count = df.groupby("column1")["column1"].count()
#column1の特徴毎の目的変数の合計を取得する
col1_sum = df.groupby("column1")["target"].sum()
print("column1のカウント数↓" + str(col1_count))
print("column1の目的変数の合計↓" + str(col1_sum))

#column2の特徴毎のカウント数を取得する
col2_count = df.groupby("column2")["column2"].count()
#column2の特徴毎の目的変数の合計を取得する
col2_sum = df.groupby("column2")["target"].sum()
print("column2のカウント数↓" + str(col2_count))
print("column2の目的変数の合計↓" + str(col2_sum))</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

column1のカウント数↓column1
A    5
B    5
Name: column1, dtype: int64
column1の目的変数の合計↓column1
A    2
B    3
Name: target, dtype: int64
column2のカウント数↓column2
C    1
D    7
E    2
Name: column2, dtype: int64
column2の目的変数の合計↓column2
C    1
D    3
E    1
Name: target, dtype: int64</pre><p>
次に、関数を定義します。DataFrameのapplyメソッドは、行または列毎に処理を行うことができます。また、その処理は関数で定義することができます。今回定義するleave_te関数は、先に用意した特徴毎の目的変数の合計から自身の目的変数を引き、特徴毎のカウント数から1を引いています。その値から作成した平均値をtarget_encodingの値として取得しています。</p><pre class="crayon-plain-tag">#[IN]:

#特徴毎の合計とカウント数から自身の値を引く関数を作成
def leave_te(row, count, sum):
  #特徴の名前を取得
  col = count.name
  #特徴の目的変数の合計から自身の目的変数を引く
  x = sum[row[col]] - row["target"]
  #特徴のカウント数から1(自身のカウント数)を引く
  y = count[row[col]] - 1
  #平均を返す
  return x / y

#leave_te関数を適用する
df["column1_target"] = df.apply(leave_te, args = (col1_count, col1_sum), axis = 1)
df["column2_target"] = df.apply(leave_te, args = (col2_count, col2_sum), axis = 1)
df</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-24-16.14.11.png" alt="" /><br />
Leave-one-out Target Encodingが実装できました。しかし、今回もリークが発生しています。まず一番分かりやすいのはcolumn2_targetの8、9行目です。column2_targetの値が目的変数と逆の値を取っていることが分かると思います。これは自身の目的変数を計算に含まなかったことにより、逆に強く影響が生まれてしまっている例です。</p>
<p>column1_targetの0~4行目も見てみます。これらは一見すると問題なさそうですが、図5のような問題が発生しています。</p>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/06/図5-1.png" alt="" /></p>
<p>図5に示されているように複数個のカテゴリカル変数があったとしても、Leave-one-out Target Encodingによって目的変数がわかってしまう状態になっています。テストデータでは訓練データ全体の平均が与えられるわけですので、これらの値に適合したモデルでは高い評価を出すことができないと予想できます。</p>
<h4>Holdout Target Encoding</h4>
<p>次に、Holdout Target Encoding（Holdout Target Statistic）です。この方法は訓練データを分割し、別ブロックの目的変数の平均を使用して自身のブロックのカテゴリカル変数を変換します。本稿で紹介するTarget Encodingの中ではもっとも一般的に知られている方法です。絶対ではありませんが、この方法を使用するとリークを起こりにくくすることができます。図6は図特徴量の1つに焦点を当てた時のHoldout Target Encodingです。</p>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/06/図6-1.png" alt="" /></p>
<p>図6を見ると、自身のブロック以外の目的変数の平均を取得していることがわかると思います。それではHoldout Target Encodingを実装していきます。今までと同様にデータセットの準備から始めます。配列を使用するためにnumpyと、分割を行うためにscikit-learnのKFoldを追加でインポートしています。</p><pre class="crayon-plain-tag">#[IN]:

#ライブラリのインポート
import pandas as pd
import numpy as np
from sklearn.model_selection import  KFold
import warnings
warnings.filterwarnings('ignore')

#DataFrameで仮のデータセットを作成（10×3）
df = pd.DataFrame({
    "column1" : ["A","A","A","A","A","B","B","B","B","B"],
    "column2" : ["C","D","D","D","D","D","D","D","E","E"],
    "target" : [1, 0, 0, 1, 0, 0, 1, 1, 0, 1]
})
df</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-24-16.14.20.png" alt="" /><br />
ここからがHoldout Target Encodingの実装の肝です。詳細な解説はコード内にも記載しますが、重要なのはKfoldでの分割です。今回はデータ数が10と非常に少ないため分割を3で行なっていますが、通常のデータセットであればもう少し分割数は増やすことをお勧めします。実際はデータセットによるため、確実な分割数を述べることはできませんが、筆者の感覚ではいつも5分割程度に設定しています。</p><pre class="crayon-plain-tag">#[IN]:

#Holdout Target Encodingの関数定義
def Holdout_te(df,column,target):
  #Kfoldの定義
  kf = KFold(n_splits = 3, shuffle = True, random_state = 2)
  #仮の箱を用意
  box = np.zeros(len(df))
  #Nanで埋めておく
  box[:] = np.nan
  #繰り返しながらTarget Encodingを行う
  for idx1, idx2 in kf.split(df):
    #２分割
    train = df.iloc[idx1]
    val = df[column].iloc[idx2]
    #グループごとの平均を計算
    mean = train.groupby(column)[target].mean()
    #boxにカテゴリ毎の平均を入れる
    for i,m in mean.iteritems():
      for v in val.index:
        if val[v] == i: box[v] = m
  #新たな列に挿入
  df[column + "_target"] = box
  return df

df=Holdout_te(df, "column1", "target")
df=Holdout_te(df, "column2", "target")
df</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-24-16.14.35.png" alt="" /><br />
Holdout Target Encodingが実装できました。結果を見ていただければわかるようにどの値も目的変数を一意には説明していません。このようにHoldout Target Encodingを使用することでリークを起こりにくくすることができます。</p>
<p>分割の仕方によっては、相手ブロックに計算を行うための変数が存在せず、NaNが発生する場合があります。この欠損部分に関しても埋め方は様々ですが、一旦同じ特徴量の平均値で埋めておきます。特徴量などでの欠損値の処理方法に関しては以下の記事も参考にしてみてください。（参考：<a href="https://www.codexa.net/missing_value_python/" target="_blank" rel="noopener">欠損値とは？Pythonを使って欠損値の処理方法と実装を徹底解説【機械学習 入門編】</a>）</p><pre class="crayon-plain-tag">#[IN]:

#欠損を平均値で埋める
df["column2_target"] = df["column2_target"].fillna(df["column2_target"].mean())
df</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-24-16.14.49.png" alt="" /><br />
ここまで、Target Encodingについて様々な方法を解説してきました。ここまで押さえれば基本的なTarget Encodingを扱えるようになると思います。本稿では2値分類を例に挙げていますが、これが多クラスの場合もほぼ同様です。それぞれのクラスであるか否かを0と1の2値分類で考えます。回帰問題の場合も目的変数の平均を取得するのが一般的です。</p>
<h3>Smoothing</h3>
<p>SmooothingとはTarget Encodingのリークを防ぐためのテクニックを指します。リークが起こりにくいと解説したHoldout Target Encodingでも、特徴の数が少ないなどの場合にはうまく変換できないという問題があります。コンペティションで上位に入る方の中には、こういった問題を解決するとともに、さらにリークを防ぐ工夫をされている方もいます。以下に記載したkaggleのnotebookにはTarget Encoding時に重みづけを行なったり、ノイズを付加するコードが記載されています。初心者の方がここまで実装するのは難しいかもしれませんが、次のステップとしてこういった上位の方のコードを参考にすることはとてもお勧めです。（参考：<a href="https://www.kaggle.com/ogrellier/python-target-encoding-for-categorical-features" target="_blank" rel="noopener">Python target encoding for categorical features</a>）</p>
<h3>タイタニックでTarget Encodingの実装</h3>
<p>本節では実際のデータセットを用いて、Target Encodingの実装を行なっていきます。今後、コンペティションなどに参加される際は、本節の実装を参考にしていただけたら幸いです。Target Encodingの方法としてはHoldout Target Encodingを用います。なお、今回はTarget Encodingの実装に重きをおいているので、適切なカテゴリカル変数を見つけ出す手順は省いています。特徴選択に関しては以下を参考にしてください。まずは必要なライブラリをインポートします。（参考：<a href="https://www.codexa.net/feature-selection-methods/" target="_blank" rel="noopener">特徴選択とは？機械学習の予測精度を改善させる必殺技「特徴選択」を理解しよう</a>）</p><pre class="crayon-plain-tag">#[IN]:

#ライブラリの読み込み
import pandas as pd
import numpy as np
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.model_selection import  KFold
import warnings
warnings.filterwarnings('ignore')</pre><p>
データセットにはkaggleのチュートリアルで最も有名なTitanicのデータセットを利用します。Titanicのデータセットはseaborn上に用意されているので、今回はそちらを利用します。kaggle上で提供されるTitanicとseaborn上のTitanicのデータセットは若干の違いがあるため、注意してください。</p>
<p>Titanicコンペティションの解説はAIマガジンの<a href="https://www.codexa.net/kaggle-titanic-beginner/" target="_blank" rel="noopener">【Kaggle初心者入門編】タイタニック号で生き残るのは誰？</a>でも紹介しています。今回は投稿は行わないため、投稿まで行いたい方はこの記事を参考に挑戦してみて下さい。kaggleのタイタニックのページは以下になります。（参考：<a href="https://www.kaggle.com/c/titanic" target="_blank" rel="noopener">Titanic &#8211; Machine Learning from Disaster</a>）</p><pre class="crayon-plain-tag">#[IN]:

#titanicデータセットを読み込んで、一部を表示
df = sns.load_dataset('titanic')
df.head()</pre><p>
<img loading="lazy" class="" src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-24-16.14.58.png" alt="" width="1064" height="233" /><br />
データセットの最初5行が表示されました。今回はTarget Encodingの実装が目的なため、データセットの中からカテゴリカル変数であるものを抜き出します。今回はsurvived（目的変数）、sex（性別）とembark_town（出航地名）を使用します。</p><pre class="crayon-plain-tag">#[IN]:

#survived（目的変数）、sex（性別）、embark_town（出航地名）を抜き出して表示
df = df[["survived", "sex", "embark_town"]]
df</pre><p>
<img loading="lazy" class="" src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-24-16.26.15.png" alt="" width="313" height="400" /><br />
survived（目的変数）、sex（性別）、embark_town（出航地名）を抜き出すことができました。念のため、欠損値を確認して、存在していれば削除してしまいます。</p><pre class="crayon-plain-tag">#[IN]:

#欠損値の有無を確認
df.isnull().sum()</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

survived       0
sex            0
embark_town    2
dtype: int64</pre><p>
</p><pre class="crayon-plain-tag">#[IN]:

#欠損値を含む行を削除して、表示
df = df.dropna()
df</pre><p>
<img class="" src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-24-16.15.09.png" alt="" /><br />
データセットを訓練用データとテスト用データに分けます。通常テストデータは目的変数が分からない状態で存在することがほとんどですが、今回はテストデータを別で用意していないので、データセットから分割したデータをテストデータとして扱います。テストデータの割合は2割とし、目的変数の割合が等しくなるように分割します。</p><pre class="crayon-plain-tag">#[IN]:

#目的変数の割合が等しくなるようにデータセットを分割
train, test = train_test_split(df, test_size = 0.2, shuffle = True, stratify = df["survived"], random_state = 0)

print("訓練用データは" + str(len(train)))
print("テスト用データは" + str(len(test)))</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

訓練用データは711
テスト用データは178</pre><p>
訓練データが711行、テストデータが178行の2つのデータに分けることができました。それではこれらのデータを用いてHoldout Target Encodingを行なっていきます。前節でも使用した関数を定義します。今回はデータ数もある程度存在するのでsplit数を5に設定しています。</p><pre class="crayon-plain-tag">#[IN]:

#Holdout Target Encodingの関数定義
def Holdout_te(df,column,target):
  #Kfoldの定義
  kf = KFold(n_splits = 5, shuffle = True, random_state = 2)
  #仮の箱を用意
  box = np.zeros(len(df))
  #Nanで埋めておく
  box[:] = np.nan
  #繰り返しながらTarget Encodingを行う
  for idx1, idx2 in kf.split(df):
    #２分割
    train = df.iloc[idx1]
    val = df[column].iloc[idx2]
    #グループごとの平均を計算
    mean = train.groupby(column)[target].mean()
    #boxにカテゴリ毎の平均を入れる
    for i,m in mean.iteritems():
      for v in val.index:
        if val[v] == i: box[v] = m
  #新たな列に挿入
  df[column + "_target"] = box
  return df

#indexを振り直す
train = train.reset_index().drop("index", axis = 1)
#Holdout Target Encodingの実装
train = Holdout_te(train, "sex", "survived")
train = Holdout_te(train, "embark_town", "survived")
train</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/08/スクリーンショット-2021-08-30-11.19.37.png" alt="" /><br />
訓練データのHoldout Target Encodingが完了しました。次にテストデータのTarget Enodingを行います。こちらは非常に単純で、訓練データの目的変数の平均を取得するだけで問題ありません。</p><pre class="crayon-plain-tag">#[IN]:

#訓練用データのsexごとの目的変数の平均を取得
column1 = train.groupby("sex")["survived"].mean()
#訓練用データのembark_townごとの目的変数の平均を取得
column2 = train.groupby("embark_town")["survived"].mean()

#テストデータへの適用
test["sex_target"] = test["sex"].map(column1)
test["embark_town_target"] = test["embark_town"].map(column2)
test</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/08/スクリーンショット-2021-08-30-11.20.21.png" alt="" /><br />
テストデータに対してもTarget Encodingを適用することができました。本節の最初でも述べたように「sex」と「embark_town」に対してTarget Encodingを適用することが正しいかどうかはテストデータの精度が向上し、リークしていないことを確認できた時点で初めて評価できます。この辺りのさじ加減は非常に難しいですが、まずは使ってみることが大切です。データ分析の前処理の仕方は山ほどありますので、試しながら自身の引き出しを増やしていきましょう。</p>
<h3>まとめ</h3>
<p>いかがでしたでしょうか。現在、データ分析コンペティションは様々な場所で開かれています。是非、調べて参加してみてください。本稿で取り上げた手法が、少しでも皆様のお役に立てたら幸いです。</p>
<p>codexaでは機械学習初学者に向けたコースを複数提供しています。</p>
<ul>
<li><a href="https://www.codexa.net/numpy/" target="_blank" rel="noopener">Numpy 入門</a></li>
<li><a href="https://www.codexa.net/matplotlib/" target="_blank" rel="noopener">Matplotlib 入門</a></li>
<li><a href="https://www.codexa.net/pandas/" target="_blank" rel="noopener">Pandas 入門</a></li>
<li><a href="https://www.codexa.net/linear-basics/" target="_blank" rel="noopener">線形代数 入門</a></li>
<li><a href="https://www.codexa.net/statistics-for-machine-learning-first/" target="_blank" rel="noopener">統計入門（前編）</a></li>
<li><a href="https://www.codexa.net/statistics-for-machine-learning-second/" target="_blank" rel="noopener">統計入門（後編）</a></li>
<li><a href="https://www.codexa.net/linear-regression-for-beginner/" target="_blank" rel="noopener">線形回帰 入門</a></li>
</ul>
<p>また、すでに機械学習の基礎知識がある方に向けて、機械学習の様々な手法を詳しく解説したチュートリアルも公開しています。</p>
<ul>
<li><a href="https://www.codexa.net/tutorial-linear-regression-for-beginner/" target="_blank" rel="noopener">実践 線形回帰</a></li>
<li><a href="https://www.codexa.net/tutorial-logistic-regression-for-beginner/" target="_blank" rel="noopener">実践 ロジスティック回帰</a></li>
<li><a href="https://www.codexa.net/decision-tree-random-forest/" target="_blank" rel="noopener">決定木とランダムフォレスト</a></li>
<li><a href="https://www.codexa.net/support-vector-machine-tutorial/" target="_blank" rel="noopener">サポートベクターマシン</a></li>
<li><a href="https://www.codexa.net/naive-bayes-tutorial/" target="_blank" rel="noopener">ナイーブベイズ（単純ベイズ分類器）</a></li>
<li><a href="https://www.codexa.net/xgboost-tutorial/" target="_blank" rel="noopener">XGBoost</a></li>
<li><a href="https://www.codexa.net/keras-cnn-python/" target="_blank" rel="noopener">はじめての画像認識</a></li>
</ul>
<p>是非、皆様のご受講をお待ちしております。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.codexa.net/target_encoding/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>データ拡張（Data Augmentation）徹底入門！Pythonとkerasでデータ拡張を実装しよう</title>
		<link>https://www.codexa.net/data_augmentation_python_keras/</link>
					<comments>https://www.codexa.net/data_augmentation_python_keras/#respond</comments>
		
		<dc:creator><![CDATA[codexaチーム]]></dc:creator>
		<pubDate>Mon, 13 Sep 2021 02:00:55 +0000</pubDate>
				<category><![CDATA[機械学習]]></category>
		<guid isPermaLink="false">https://www.codexa.net/?p=3031</guid>

					<description><![CDATA[データ拡張（Data Augmentation）の基礎知識、Pythonとkerasを使用した「ImageDataGeneratorクラス」の実装方法を詳しく解説します。後半はデータ拡張を用いてCNNによるCIFAR-10の分類実装を解説。]]></description>
										<content:encoded><![CDATA[<p>何かを「集める」という作業は非常に大変です。特に画像などに関しては数枚程度であればすぐに集まりますが、数百枚、数千枚となってくると手作業ではかなり大変です。また、一般的に出回らないような画像であれば収集の制限はさらに厳しくなることが予想されます。</p>
<p><a href="https://www.codexa.net/what-is-machine-learning/">機械学習</a>、特に<a href="https://www.codexa.net/what-is-deep-learning/">ディープラーニング</a>による<a href="https://www.codexa.net/keras-cnn-python/">画像認識</a>では大量の画像データを必要とする場合が多いです。時にその量は数万枚や数十万枚（またはそれ以上）に登る場合もあります。有名なCelebAのデータセットには202,599枚もの有名人の顔画像が用意されています。しかし、全てのデータセットがこれほどの量を保持しているわけではありません。そのため、私たちは現状で用意できるデータセットからデータ数を増やす手段を考える必要があります。（参考：<a href="http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html" target="_blank" rel="noopener">CelebA Dataset</a>）</p>
<p>その手段の1つとして、本稿では、Data Augmentation（読み：データ・オーギュメンテーション、訳：データ拡張）について解説していきます。前半では「Data Augmentationの基礎知識」、中盤では「Data Augmentationの各変換の実装」、後半では実際のデータセットを用いて「ニューラルネットワークによる分類」を行います。Data Augmentationは機械学習を使用して画像認識を学びたいと考えている方にとってはとても重要な技術になりますので、しっかりとした基礎知識と実装スキルを身につけましょう。</p>
<h3>前提知識</h3>
<p>Data Augmentationの内容は機械学習で画像を扱う上では一部分でしかありません。そのため本稿を読まれる際には、画像認識の大枠や用語などの基礎知識（ディープラーニングやCNNなど）があることが望ましいです。それらの基礎知識だけでも非常に解説が長くなってしまうため、本稿では詳細には記載しません。もし、これらの知識に不安がある方は以下の記事を先に読まれることをお勧めします。本稿でも以下の記事で扱われている内容を前提に解説を進めていきます。</p>
<ul>
<li>ディープラーニング： <a href="https://www.codexa.net/what-is-deep-learning/" target="_blank" rel="noopener">今時のエンジニアが知っておくべきディープラーニングの基礎知識</a></li>
<li>CNN：<a href="https://www.codexa.net/cnn-mnist-keras-beginner/" target="_blank" rel="noopener">初心者のための畳み込みニューラルネットワーク（MNISTデータセット + Kerasを使ってCNNを構築）</a></li>
</ul>
<h3>Data Augmentationとは</h3>
<p>Data Augmentation（データ拡張）とは、学習用の画像データに対して「変換」を施すことでデータを水増しする手法です。この「変換」には様々な種類が存在します。その種類についてはこの後、実装を踏まえて解説します。まずはData Augmentationを適用した画像を確認してみましょう。それぞれの写真がどのように変換されているのかに注目してください。なお本稿で使用されている画像に関しては以下のサイトから引用しています。（参考：<a href="https://www.pakutaso.com/" target="_blank" rel="noopener">フリー写真素材ぱくたそ</a>）<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/06/図1.png" alt="" /><br />
図1にはパンケーキが表示され、図2〜5では図1のパンケーキが少し変化した状態で表示されていることが分かると思います。図2では回転、図3では左移動、図4では拡大、図5では上移動と変化しています。このように1枚の画像から４枚の画像に変換され実質5枚の画像が存在しています。これがData Augmentationによる画像の水増しです。</p>
<blockquote><p>本来、Data Augmentationには画像を「変換」する手法だけでなく、「生成」する手法も存在します。例としてはGAN（Generative Adversarial Networks）などの技術を利用したものです。しかし、これらの内容は高度な専門的知識が必要である上、本稿内に収めることが難しい内容なため、記載しません。今後、本稿でのData Augmentationは画像の「変換」を指します。皆様が学習を進めていく中で興味がでてきた方は是非調べて見てください。（参考：<a href="https://ja.wikipedia.org/wiki/%E6%95%B5%E5%AF%BE%E7%9A%84%E7%94%9F%E6%88%90%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF" target="_blank" rel="noopener">敵対的生成ネットワーク Wikipedia</a>）</p></blockquote>
<h3>Data Augmentationの必要性</h3>
<p>本節ではData Augmentationの必要性について解説していきます。可能な限り直感的な理解ができるよう、画像を用いて解説していきます。必要性については主に以下の点が挙げられます。</p>
<ul>
<li>データ数の増加</li>
<li>過学習への対策</li>
</ul>
<h4>データ数の増加</h4>
<p>近年のニューラルネットワーク（特にCNN）による画像認識技術はとても進歩しています。同時に、開発されたモデルの中にはとても深い層をもつものもあります。そういったモデルの中には多くのパラメータを有するものもあり、学習のために大量のデータが必要とされる場合があります。しかし、データによっては必要な数を用意できない場合もあります。特に医療系などのプライバシーに関わるようなデータは比較的集めにくい現状があります。少ないデータ数でも学習を行えるようにするためにData Augmentationは必要なのです。実際に画像がどのように増えて見えるのかに関してはこのあとの「Data Augmentation一覧」で確認できます。</p>
<h4>過学習への対策</h4>
<p>もう一つは過学習への対策です。過学習に関する詳細は本稿では省きますが、簡単に述べると「学習用データに機械学習モデルが適合しすぎることにより、テストデータに対する適合率が下がる」ことを指します。私たちが画像認識のモデルを作成する根本的な目的は、学習したモデルを用いて、未知のデータを正しく分類・評価することです。そのため、どれだけ学習用データに対して高精度のモデルが完成したとしても、テストデータに対して精度が低ければ意味がありません。どのような例が考えられるのか、画像を使用しながら解説します。仮定する目的は「犬と猫を分類すること」とします。与えられる学習用データセットには図6、図7のような犬と猫の画像が存在しているとします。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/06/図3.png" alt="" /><br />
上の画像は左が猫、右が犬の画像です。猫は左に、犬は右に体を向けています。もし、学習用データセット内の全ての犬猫が同じ体の向きだった場合、起こりうる問題として次のことが挙げられます。</p>
<ul>
<li>左向きの犬を「猫」と予測する可能性がある</li>
<li>右向きの猫を「犬」と予測する可能性がある</li>
</ul>
<p>これは学習したモデルが、犬と猫を体の向きで判断するように学習してしまった場合に起こりうる問題です。極端な例ではありますが、テスト時に逆向きの犬猫が与えられる可能性はあります。この問題に対して、Data Augmentationは比較的有効です。この問題の根本は「学習用データセット内の犬猫の向きが同じ」であることです。つまり、犬と猫が左右両方を向いている画像があれば解決する可能性が高くなります。そこで、学習用データセットにData Augmentationを適用し、犬猫の画像を反転させた画像を加えます。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/06/図5.png" alt="" /><br />
図8と図9の犬と猫の画像は図6と図7を反転させたものです。これによりモデルは学習用データセット内の画像に加えて、反転した画像も学習に使用できます。これでテスト時の画像で左右どちらの犬猫の画像が与えられたとしても、正しく分類できる確率は比較的高くなります。この考えはデータセットの規模が大きい場合にも適用できます。数が多いことと、データの質が高いことは別です。データ数が多くても、今回の仮定のように学習用データに「偏った特徴」が含まれていることもあります。</p>
<p>ここまでData Augmentationの必要性について解説してきました。今回の例はかなり極端なものでしたが、大まかな概要は掴むことができたと思います。冒頭でも述べたように、機械学習で画像認識の分野を扱いたい方にとってData Augmentationはとても重要な技術です。しかし、日常的に使用していると「なぜ」行なっているかを見失う場合があります。しっかりと必要性を理解した上で実装するように心がけましょう。</p>
<h3>Data Augmentationの注意点</h3>
<p>本節ではData Augmentationを使用する際の注意点について解説していきます。前節ではData Augmentationの必要性について記載してきましたが、常に有効であるとは限りません。有効でないData Augmentationとしては以下の点が挙げられます。</p>
<ul>
<li>データセットに合わない変換</li>
<li>過学習</li>
</ul>
<h4>データセットに合わない変換</h4>
<p>Data Augmentationは画像に対して様々な変換を施すことで、データを水増しします。しかし、考えなしで闇雲に変換すれば良いわけではありません。前節で述べたように、有効なData Augmentationを適用することは重要ですが、変換によっては逆にモデルの精度を下げる可能性もあります。次の画像にはいくつかのData Augmentationの適用例を示しています。画像を確認しながら適用されている変換が有効であるかを考えてみてくだい。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/06/図6.png" alt="" /><br />
まずは図10〜図12の電車の画像を見てみます。ここでは電車の種類が何かを分類する機械学習モデルを作成すると仮定します。図11は電車がズームされており、図12は電車が回転されています。まず図11に関してですが、電車を撮影する際のズームのレベル感は撮影者に委ねられる場合が多いです。そのため、撮影者によっては引いた写真を撮る人もいれば、フレームいっぱいに撮影する方もいるかもしれません。そのため図11は比較的有効そうです。逆に図12は画像が回転され、電車が逆さまになっています。電車を逆さまに撮る方は少ないと考えられますし、事故でなければ電車が逆さまになる現場を目にすることはほとんどないと思われます。電車の種類を考えるのであれば、逆さまの写真はそれほど有効ではなさそうです。</p>
<p>次に、図13〜図15の数字の画像を見てみます。ここでは数字が0〜9の数字に分類する機械学習モデルを作成すると仮定します。図18は数字が少し回転しており、図19は数字が反対になっています。図14は少なめの回転ですので、撮影の状況によっては考慮できるレベルです。そのため有効と考えられます。逆に、図15は数字が反対になっていますが、4の数字をこの形で書くことはありません。数字の分類に対しては有効とは考えにくいです。</p>
<p>当然ですが、本当に有効か有効でないかは、実際にモデルで分類を行い、評価をしない限りは判断できません。しかし図12や図15など明らかに違和感のある画像に変換しない（または気づく）ことは重要です。有効でないData Augmentationはモデルの精度を著しく下げてしまう場合もあるので注意しましょう。</p>
<h4>過学習</h4>
<p>もう一つの注意点は過学習です。「Data Augmentationの必要性」ではData Augmentationは過学習を防ぐために有効であると記載しましたが、逆もあります。学習用データに「似た特徴」を与えてしまう可能性があることです。画像の変換によって増えた画像はある程度似た画像になります。そのため、モデルがそれらの画像に過剰に適合すると、過学習を引き起こす可能性があります。Data Augmentationは少ないデータ数でも学習を可能にするための手段として有効です。しかし、過学習を避けながら、複雑なモデルを使用する場合や高精度の評価を得たい場合には質のいいデータがある程度存在することが望ましいです。</p>
<p>ここまでData Augmentationの注意点について解説してきました。「Data Augmentationの必要性」と合わせて理解することができたでしょうか。実際、このような欠点に関する理解は様々なデータセットで練習し、実践を積むことが最も効果的です。本稿の後半でも実際のデータセットを用いますので、そちらも参考にしながら学習を進めてみてください。興味のある方はkaggleなどのデータ分析コンペティションなどのデータセットを使用してみるのも1つの手だと思います。（参考：<a href="https://www.codexa.net/what-is-kaggle/">kaggleとは？</a>）</p>
<h3>Data Augmentationのタイミング</h3>
<p>本節ではData Augmentationが実際に適用されるタイミングについてお伝えします。今後Data Augmentationを実装していく際は、keras（本稿でも使用）やPyTorch（以下の記事を参考）を使うことが多くなるかと思います。その際に感じるのが、Data Augmentationがどのタイミングで行われているか分かりにくいということです。さらに、その部分が分からないまま学習を進めるとData Augmentationを間違えて理解してしまう可能性があります。少し難しい部分もありますので、図を用いながら可能な限り直感的に理解できるように解説していきます。（参考：<a href="https://www.codexa.net/pytorch-python/" target="_blank" rel="noopener">PyTorch 入門！人気急上昇中のPyTorchで知っておくべき6つの基礎知識</a>）</p>
<h4>オフライン拡張</h4>
<p>1つ目はオフライン拡張（Offline Augmentation）です。オフライン拡張はデータセットに存在する画像自体にData Augmentationを適用し、単純に画像の枚数を増やす手法です。Data Augmentationを初めて聞いた方が真っ先に湧くイメージだと思います。図16はオフライン拡張の内容を示しています。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/06/図7.png" alt="" /><br />
図16はデータセット内の各画像に対して回転の変換を加えた場合です。各画像1枚ずつの変換画像が出来上がるため、データセットは2倍になっています。これがオフライン拡張です。比較的小さなデータセットの場合に適応される場合があります。筆者自身はこの手法を扱った経験はありませんが、データ分析コンペティションなどで用意されたデータセットの中で既にオフライン拡張が行われていた経験はあります。</p>
<p>オフライン拡張には注意点があります。それはデータセット自体の容量が単純に増加するという点です。大量のデータを扱うディープラーニングでは、学習に必要なデータセットの容量はとても大きくなります。特に画像などのデータセットを扱う場合にはテーブルデータなどに比べて容量も大きくなりやすいです。オフライン拡張を行うということは画像データの増加に比例して容量も増加します。そのため、大容量のデータを保存できる領域が必須になります。保存領域には限りがあるのが一般的ですので、オフライン拡張でデータ数を増やす際には注意しなければなりません。</p>
<h4>オンライン拡張</h4>
<p>2つ目はオンライン拡張（Online Augmentation）です。記事によってはオンザフライ拡張（On-the-fly Augmentation）とも呼ばれていますが本稿ではオンライン拡張という言葉を使用します。オンライン拡張はオフライン拡張に比べて比較的一般的です。図17はオンライン拡張の手順を示しています。図と解説を照らしわあせながら理解しましょう。<br />
<img class="alignleft" src="https://www.codexa.net/wp-content/uploads/2021/06/図8-1.png" alt="" /><br />
オンライン拡張を理解するためにはミニバッチ学習という言葉を理解する必要があります。簡単に記載すると、データセットを複数に分割したものを使用して学習を行うことです。一般的にディープラーニングではモデルを学習させる際にデータセットを複数のミニバッチに分割し、ミニバッチごとに学習を行います。オンライン拡張は、モデルに入力するミニバッチに対してData Augmentationを適用します。</p>
<p>オンライン拡張ではデータセット自体の容量は増えません。オフライン拡張ではData Augmentationをランダムに設定してもデータ数は指定した数にしかなりませんが、オンライン拡張であれば学習毎にランダムな画像が生成されます。エポックを複数にして学習を行えば、同じミニバッチでも違う画像を使用することができます。これにより、モデルからするとより多くの画像を学習に使用することができることになります。</p>
<p>ここまで、Data Augmentationについて解説してきました。深い内容ですので、一度読んだだけで完全に理解するのは難しいかもしれません。そういった方は本稿を一通り読んでいただき、実装まで確認した上でもう一度本節を読み直していただくことをお勧めします。</p>
<h3>Data Augmentation一覧</h3>
<p>本節ではData Augmentationにおける画像変換の一覧を解説とともに実装します。様々な変換がありますが、1つ1つ順番に見ていくことで実際のデータセットに適応させる際もイメージがつきやすくなります。特にパラメータが存在する変換は値によって変換の度合いが決まります。Data Augmentationを使いこなすためにも、確実に理解しておきましょう。</p>
<p>実装はkerasと呼ばれるニューラルネットワークのライブラリを用います。Google Colabを用いて実装していきます。ライブラリをそのままインポートすれば同じように実装可能です。是非、ご自身でも実装してみてください。Google Colabを使用したことがない方は下記の記事を参考にしてください。（参考：<a href="https://www.codexa.net/how-to-use-google-colaboratory/" target="_blank" rel="noopener">Google Colabの知っておくべき使い方 – Google Colaboratoryのメリット・デメリットや基本操作のまとめ</a>）</p>
<p>最初の設定しとして、Google Colabの上部タブから「ランタイム」の「ランタイムの変更」を選択してください。そこから、「ハードウェアアクセラレータ」を「GPU」に変更してください。本節では使用しませんが、次節で使用するため、現段階で変更しておきます。変更ができたら、まずは必要なライブラリをインポートします。</p><pre class="crayon-plain-tag">#[IN]:

#必要なライブラリのインポート
import numpy as np
import matplotlib.pyplot as plt
from keras.preprocessing import image
from keras.preprocessing.image import ImageDataGenerator,array_to_img,img_to_array</pre><p>
<h4>画像の準備</h4>
<p>まずはGoogle Colab上に画像を読み込みます。本稿と同じ画像（<a href="https://www.pakutaso.com/20200607170post-28044.html" target="_blank" rel="noopener">パンケーキの画像</a>）を使用したい方は以下のサイトから利用規約に同意した上で画像をダウンロードしてください。アップロードが完了したら「パスをコピー」を選択してコード上に貼り付けてください。画像に関してはkerasのload_imgメソッドを用いて640×640にリサイズさせたものを読み込みます。（参考：<a href="https://www.pakutaso.com/userpolicy.html" target="_blank" rel="noopener">フリー写真素材ぱくたそ（利用規約）</a>）</p><pre class="crayon-plain-tag">#[IN]:

#アップロードされた画像を読み込み
img = image.load_img("/content/ogasuta458A8104_TP_V4.jpg", target_size=(640, 640))
#画像をnumpy配列に変換する
img = np.array(img)
#表示画像のサイズを設定
plt.figure(figsize = (10, 10))
#軸を表示しない
plt.xticks(color = "None")
plt.yticks(color = "None")
plt.tick_params(bottom = False, left = False)
#表示
plt.imshow(img)</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-03-16.01.23.jpg" alt="" /></p>
<h4>ImageDataGeneratorクラスについて</h4>
<p>各変換に必要なImageDataGeneratorクラスについて解説します。ImageDataGeneratorはkerasが持つData Augmentationを行うためのクラスです。本稿ではこちらを用いてData Augmentationを実装していきます。ImageDataGeneratorクラスには様々な画像の変換がまとめて実装できるようになっています。以下のコードはImageDataGeneratorクラスのデフォルト引数になります。この中の引数の内、比較的よく使うものを中心に解説していきます。</p><pre class="crayon-plain-tag">#[IN]:

#○がついている引数を本稿では扱う
ImageDataGenerator(
    featurewise_center = False,#○
    samplewise_center = False,#○
    featurewise_std_normalization = False,#○
    samplewise_std_normalization = False,#○
    zca_whitening = False,
    zca_epsilon = 1e-06,
    rotation_range = 0,#○
    width_shift_range = 0.0,#○
    height_shift_range = 0.0,#○
    brightness_range = None,#○
    shear_range = 0.0,#○
    zoom_range = 0.0,#○
    channel_shift_range = 0.0,#○
    fill_mode = "nearest",#○
    cval = 0.0,#○
    horizontal_flip = False,#○
    vertical_flip = False,#○
    rescale = None,#○
    preprocessing_function = None,#○
    data_format = None,
    validation_split = 0.0,
    dtype = None,
)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

&lt;keras.preprocessing.image.ImageDataGenerator at 0x7f5a4e97f810&gt;</pre><p>
<h5>ImageDataGeneratorのメソッド</h5>
<p>ImageDataGeneratorクラスはメソッドを使用してデータを受け取り、Data Augmentationを適用します。適用するときのメソッドは与えられるデータ形式によります。データに合わせて利用してください。本稿では画像はnumpyの配列として与えられているので、flowメソッドを使用します。これらのメソッドにも引数があります。また、他のメソッドも存在しますが、本稿の目的とは少しずれてしまうため、主要なメッソド以外は記載しません。ImageDataGeneratorクラスの他のメソッドに興味がある方はkerasの公式ドキュメントを参考にしてみてください。（参考：<a href="https://keras.io/ja/preprocessing/image/" target="_blank" rel="noopener">keras公式：ImageDataGenerator</a>）</p>
<ul>
<li>flowメソッド→numpyのデータを受け取り、データのバッチを返す（本稿で使用）</li>
<li>flow_from_dataframeメソッド→pandasのデータフレームを受け取り、データのバッチを返す</li>
<li>flow_from_directryメソッド→ディレクトリ内のパスを受け取り、データのバッチを返す</li>
</ul>
<h4>画像表示の準備</h4>
<p>ここから、表示したパンケーキの画像に対して、様々な変換を行っていきます。実際に画像処理を行う前に、画像表示用の関数を定義しておきます。本稿では各変換に対して6枚の変換後の画像が表示されるようにします。これは変換がランダムで行われるため、1枚では変換していない画像が表示されてしまう可能性があるためです。flowメソッドのseed値は固定していますので、表示される6枚の画像は何度同じセルを実行しても同様のものになります。</p><pre class="crayon-plain-tag">#[IN]:

#画像表示用の関数を定義
def show(datagen, img):
  #表示サイズを設定
  plt.figure(figsize = (10, 5))
  
  #画像をbatch_sizeの数ずつdataに入れる
  #本稿は画像が一枚のため同じ画像がdataに入り続けることになる
  for i, data in enumerate(datagen.flow(img, batch_size = 1, seed = 0)):
    #表示のためnumpy配列からimgに変換する
    show_img = array_to_img(data[0], scale = False)
    #2×3の画像表示の枠を設定＋枠の指定
    plt.subplot(2, 3, i+1)
    #軸を表示しない
    plt.xticks(color = "None")
    plt.yticks(color = "None")
    plt.tick_params(bottom = False, left = False)
    #画像を表示
    plt.imshow(show_img)
    #6回目で繰り返しを強制的に終了
    if i == 5:
      return</pre><p>
次にパンケーキの画像配列(img)に次元を1つ追加します。これはこの後使用するImageDataGeneratorクラスの入力が4次元である必要があるためです。現在imgの配列は640×640×3（縦×横×チャンネル数）になっています。これを1×640×640×3にします。この「1」は「データセット内の何番目の画像ですか？」という情報です。今回のデータセットには画像が1枚しかないと仮定するので1を追加します。</p><pre class="crayon-plain-tag">#[IN]:

#パンケーキの画像配列の形
print(img.shape)
#配列に次元を追加
img_cake=img[np.newaxis, :, :, :]
#次元追加後の配列の形
print(img_cake.shape)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

(640, 640, 3)
(1, 640, 640, 3)</pre><p>
<h4>画像の回転（rotation_range）</h4>
<p>最初の変換は、画像の回転です。その名の通り画像を回転させます。まず、確認していただきたいのは画像の出力が同じではないことです。「オンライン拡張」でも少し触れましたが、同じバッチでも違う変換が行われています。そのため、1枚の画像が6枚になっているように見えると思います。これらがモデルが学習している画像ということです。これは他の変換でも同様ですので、よく理解しておいてください。画像の回転の引数はrotation_rangeで設定されます。</p>
<ol>
<li>int型（180）の場合→指定された角度（-180度〜180度）の範囲でランダムに回転を行います。</li>
</ol>
<pre class="crayon-plain-tag">#[IN]:

#-180度〜+180度の間でランダムに回転するImageDataGeneratorを作成
rotation_datagen = ImageDataGenerator(rotation_range = 180)
#画像を表示
show(rotation_datagen, img_cake)</pre>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-03-16.03.13.png" alt="" /></p>
<h4>左右平行移動（width_shift_range）</h4>
<p>次に、画像の左右平行移動です。引数はwidth_shift_rangeで設定します。width_shift_rangeの引数はいくつか種類がありますので、実装時に迷わないようにしっかりと理解しておきましょう。</p>
<ol>
<li>int型（50）の場合→指定されたピクセル（-50〜+50）の範囲で左右にランダムに動かします。</li>
<li>list型（[50,100]）の場合→指定されたピクセル（-100,-50,+50,+100）の内、左右にランダムに動かします。</li>
<li>float型（0.5）の場合→指定された値×画像の横幅（-320〜+320）の範囲で左右にランダムに動かします。</li>
</ol>
<pre class="crayon-plain-tag">#[IN]:

#-320〜+320の間でランダムに左右平行移動するImageDataGeneratorを作成
width_datagen = ImageDataGenerator(width_shift_range = 0.5)
show(width_datagen, img_cake)</pre>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-03-16.03.28.png" alt="" /><br />
上の条件に従って左右平行移動が適用されます。左右平行移動は値によっては画像が枠内からが飛び出す場合があります。その場合、枠内の画像は補間の必要があります。補間の設定に関しては後述しますので、現段階では補間が行われているということを知っておいてください。</p>
<h4>上下平行移動（height_shift_range）</h4>
<p>次に、画像の左右平行移動です。引数はheight_shift_rangeで設定します。height_shift_rangeの引数はwidth_shift_rangeと同様にいくつかの種類があります。設定の仕方はほぼ同様です。</p>
<ol>
<li>int型（50）の場合→指定されたピクセル（-50〜+50）の範囲で上下にランダムに動かします。</li>
<li>list型（[50,100]）の場合→指定されたピクセル（-100,-50,+50,+100）の内、上下にランダムに動かします。</li>
<li>float型（0.5）の場合→指定された値×画像の縦幅（-320〜+320）の範囲で上下にランダムに動かします。</li>
</ol>
<pre class="crayon-plain-tag">#[IN]:

#-320〜+320の間でランダムに上下平行移動するImageDataGeneratorを作成
height_datagen = ImageDataGenerator(height_shift_range = 0.5)
show(height_datagen, img_cake)</pre>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-03-16.03.53.png" alt="" /></p>
<h4>拡大と縮小（zoom_range）</h4>
<p>次に、画像の拡大と縮小です。引数はzoom_rangeで設定します。zoom_rangeの値だけでは画像がどの程度拡大又は縮小されているか理解しにくいため、実際に複数の値を試してみることをお勧めします。</p>
<ol>
<li>float型（0.5）の場合→「1-指定された値」（0.5）〜「1+指定された値」（1.5）の範囲で拡大又は縮小します。</li>
<li>list型（[0.5,1.5]）の場合→指定された値（0.5〜1.5）の範囲で拡大又は縮小します。</li>
</ol>
<pre class="crayon-plain-tag">#[IN]:

#0.5〜1.5の間でランダムに拡大又は縮小するImageDataGeneratorを作成
zoom_datagen = ImageDataGenerator(zoom_range = [0.5, 1.5])
show(zoom_datagen, img_cake)</pre>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-03-16.04.06.png" alt="" /></p>
<h4>画像のせん断（shear_range）</h4>
<p>次に、画像のせん断です。せん断とは、四角形の画像を平行四辺形に変形する処理です。引数はshear_rangeで設定します。せん断についてより詳しく知りたい方は以下のサイトも参考にしてみてください。引数のshare_rangeにはシアー角度を設定します。（参考：<a href="https://ja.wikipedia.org/wiki/%E3%81%9B%E3%82%93%E6%96%AD%E5%86%99%E5%83%8F" target="_blank" rel="noopener">せん断写像 Wikipedia</a>）</p>
<ol>
<li>float型（30）の場合→指定されたシアー角度（30度）でせん断します。</li>
</ol>
<pre class="crayon-plain-tag">#[IN]:

#30度の範囲でランダムにせん断するImageDataGeneratorを作成
shear_datagen = ImageDataGenerator(shear_range = 30)
show(shear_datagen, img_cake)</pre>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-03-16.04.24.png" alt="" /></p>
<h4>画像の補間方法（fill_mode）</h4>
<p>ここまで、画像の回転、平行移動、拡大と縮小、せん断まで解説してきました。画像の回転の際に少し触れましたが、入力画像が枠内に合わない場合の補間方法にはいくつかの種類が存在します。全部で4種類存在しますので、把握しておきましょう。引数はfill_modeで設定します。</p>
<ol>
<li>「nearest」→一番近くの画素値で補間（デフォルト）</li>
<li>「constant」→定数で補間</li>
<li>「reflect」→反転して補間</li>
<li>「wrap」→繰り返しで補間</li>
</ol>
<h5>1.nearest</h5>
<p>ImageDataGeneratorにおいてデフォルトで設定されている画像の補間になります。「aaaaaaa|abcd|ddddddd」のように一番近い画素値で外側を補完する方法です。筆者も何か理由がない限りは基本デフォルトであるnearestの補間を使用しています。</p><pre class="crayon-plain-tag">#[IN]:

#nearestで補間するImageDataGeneratorを作成
nearest_datagen = ImageDataGenerator(width_shift_range = 0.5, fill_mode = "nearest")
show(nearest_datagen, img_cake)</pre><p>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-03-16.04.34.png" alt="" /></p>
<h5>2.constant</h5>
<p>constantは定数を指定して補間する方法です。「fill_mode = constant」で実装でき、「xxxxxxx|abcd|xxxxxxx」のように画像にかかわらず特定の値で補間できます。今回の実装例では0に設定することで黒にしています。補間する際の値の指定は「cval」引数を追加することで実現できます。補間を黒に設定することで、テストの際など、画像がどの程度傾いているかなど非常にわかりやすく表示することができます。</p><pre class="crayon-plain-tag">#[IN]:

#constantで補間するImageDataGeneratorを作成
constant_datagen = ImageDataGenerator(width_shift_range = 0.5, fill_mode = "constant", cval = 0)
show(constant_datagen, img_cake)</pre><p>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-03-16.04.43.png" alt="" /></p>
<h5>3.reflect</h5>
<p>reflectは画像に対して反転した画素で補間する方法です。「fill_mode = reflect」で実装でき、「abcddcba|abcd|dcbaabcd」のように反転された画像が映ります。筆者はあまり使用した経験はありませんが、条件によっては精度向上が見込める補間方法です。</p><pre class="crayon-plain-tag">#[IN]:

#reflctで補間するImageDataGeneratorを作成
reflect_datagen = ImageDataGenerator(width_shift_range = 0.5, fill_mode = "reflect")
show(reflect_datagen, img_cake)</pre><p>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-03-16.04.53.png" alt="" /></p>
<h5>4.wrap</h5>
<p>wrapは画像に対して繰り返すような画素で補間する方法です。「fill_mode = wrap」で実装でき、「abcdabcd|abcd|abcdabcd」のように反転された画像が映ります。こちらも筆者はあまり使用した経験はありませんが、状況によっては必要になる場面もありますので理解は必要です。</p><pre class="crayon-plain-tag">#[IN]:

#wrapで補間するImageDataGeneratorを作成
wrap_datagen = ImageDataGenerator(width_shift_range = 0.5, fill_mode = "wrap")
show(wrap_datagen, img_cake)</pre><p>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-03-16.05.04.png" alt="" /></p>
<h4>左右反転（horizontal_flip）</h4>
<p>次に、画像の左右反転です。引数はhorizontal_flipで設定します。左右反転は比較的よく使用される変換です。初学者の方でも理解しやすく、筆者も画像処理を行う際に、最初に試すことが多い変換です。実装自体も簡単なので、確実に押さえておきましょう。</p>
<ol>
<li>bool型（True）の場合→ランダムに画像を左右反転します。</li>
</ol>
<pre class="crayon-plain-tag">#[IN]:

#ランダムに画像を左右反転するImageDataGeneratorを作成
horizontal_datagen = ImageDataGenerator(horizontal_flip = True)
show(horizontal_datagen, img_cake)</pre>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-03-16.05.14.png" alt="" /></p>
<h4>上下反転（vertical_flip）</h4>
<p>次に、画像の上下反転です。引数はhorizontal_flipで設定します。画像の向きや性質によっては使うことの多い変換です。使い方は左右反転と全く同じですのですが、上下画像の反転は左右反転に比べて汎用性が低い場合が多ので注意が必要です。</p>
<ol>
<li>bool型（True）の場合→ランダムに画像を上下反転します。</li>
</ol>
<pre class="crayon-plain-tag">#[IN]:

#ランダムに画像を上下反転するImageDataGeneratorを作成
vertical_datagen = ImageDataGenerator(vertical_flip = True)
show(vertical_datagen, img_cake)</pre>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-03-16.05.26.png" alt="" /></p>
<h4>明るさの調整（Brightness_range）</h4>
<p>次に、明るさの調整です。引数はBrightness_rangeで設定します。画像の明るさを変更できるため、画像自体が暗すぎる場合や明るすぎる場合に便利です。後述するチャンネルシフトと非常に似ていますが、扱いに関してはこちらの方が比較的簡単です。</p>
<ol>
<li>Tuple型（(0.3,0.8)）→指定した値の範囲（0.3〜0.8）でランダムに明るさを調整（1.0以下は暗く、1.0以上は明るくなる）</li>
<li>list型（[0.3,0.8]）→指定した値の範囲（0.3〜0.8）でランダムに明るさを調整（1.0以下は暗く、1.0以上は明るくなる）</li>
</ol>
<pre class="crayon-plain-tag">#[IN]:

#画像の明るさを0.3〜0.8の間で調整（暗くする）
brightness_datagen = ImageDataGenerator(brightness_range = [0.3, 0.8])
show(brightness_datagen, img_cake)</pre>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-03-16.05.37.png" alt="" /></p>
<h4>チャンネルシフト（channel_shift_range）</h4>
<p>次はチャンネルシフトです。チャンネルシフトとは画像を構成するRGBのチャンネルの値を変更することです。先ほどの「明るさの調整（Brightness_range）」との違いは、シフト値を指定できる点です。細かくシフト値を指定されたい方にとってはこちらの方が便利です。</p><pre class="crayon-plain-tag">#[IN]:

print("画像破裂の形は"+str(img_cake.shape))
print("1ブロックのチャンネルの値は"+str(img_cake[0][0][0]))</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

画像破裂の形は(1, 640, 640, 3)
1ブロックのチャンネルの値は[223 163 126]</pre><p>
上記に表示したのはパンケーキの画像の配列形状と1つのチャンネルの値です。チャンネルの値にはRGBの各値が入っています。チャンネルシフトはこの画素値を変更する変換です。引数はchannel_shift_rangeで設定します。RGBは256階調でプログラム上0〜255で処理されます。その際、画素値の範囲を超えるような値を指定したとしても、画素値は0または255として変換されます。</p>
<ol>
<li>float型（100.0）の場合→指定した値（-100〜+100）値でチャンネルの範囲でチャンネルをシフトします。</li>
</ol>
<pre class="crayon-plain-tag">#[IN]:

#-100〜100の間でランダムにチャンネルシフトするImageDataGeneratorを作成
channel_datagen = ImageDataGenerator(channel_shift_range = 100)
show(channel_datagen, img_cake)</pre>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-03-16.05.56.png" alt="" /></p>
<h4>画素値のリスケーリング（rescale）</h4>
<p>次は画素値のリスケーリングです。引数はrescaleで設定します。この値を設定すると他の変換を適応する前に指定した値を乗算します。画素値は通常0〜255の範囲で表されているため、それを0〜1の範囲に収めたい場合などは1/255などを乗算します。画像を元の画素値に戻したい時は、この時乗算した値の逆数を乗算することで元に戻ります。</p>
<ol>
<li>1./255の場合→他の変換を行う前に各画素値を1/255にします。</li>
</ol>
<pre class="crayon-plain-tag">#[IN]:

#各画素値を0〜1に収めるImageDataGeneratorを作成
rescale_datagen = ImageDataGenerator(rescale = 1./255)
show(rescale_datagen, img_cake)</pre>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-03-16.06.04.png" alt="" /><br />
表示される画像はほぼ黒になりましたが、それは画素値が0〜1に収まっているからです。画素値の最大値と最小値を確認すると0〜1に収まっていることが確認できます。<br />
<pre class="crayon-plain-tag">#[IN]:

#リスケーリング後の最大の画素値と最小の画素値の確認
for i, data in enumerate(rescale_datagen.flow(img_cake, batch_size = 1)):
  print(np.max(data[0]))
  print(np.min(data[0]))
  if i == 0:
    break</pre>
<pre class="crayon-plain-tag">#[OUT]:

1.0
0.015686275</pre>
<h4>データセット全体の平均を0にする（featurewise_center）</h4>
<p>次はfeaturewise_centerです。featurewise_centerは与えられたデータセット全体の入力の画素値平均を0にします。画素値のままでは負の値をとることはありませんが、featurewise_centerを使用すると負の値も持った上で画像を表現することができます。これは、ニューラルネットワークなどに対する前処理などに使用できます。画像の平均値を確認するとほぼ0になっていることが確認できます。（平均が0ちょうどにならないのは計算時に生まれる誤差のためです）</p>
<ol>
<li>bool型（True）の場合→データセット全体の画素値平均を0にします。</li>
</ol>
<pre class="crayon-plain-tag">#[IN]:

#データセット全体の平均を0にするImageDataGeneratorクラスを作成
featurewise_datagen = ImageDataGenerator(featurewise_center = True)
featurewise_datagen.fit(img_cake, seed = 0)
for i, data in enumerate(featurewise_datagen.flow(img_cake, batch_size = 1)):
  print(np.mean(data[0]))
  if i == 0:
    break</pre>
<pre class="crayon-plain-tag">#[OUT]:

-0.011399994</pre>
<h4>各サンプルの平均を0にする（samplewise_center）</h4>
<p>次はsamplewise_centerです。samplewise_centerは与えられた各サンプルの入力の画素値平均を0にします。用途としてはfeaturewise_centerとほぼ同じですが、他の条件やモデルに合わせて良い方を使用します。こちらも平均値がほぼ0になっていることが確認できます。</p>
<ol>
<li>bool型（True）の場合→画像ごとの画素値平均を0にします。</li>
</ol>
<pre class="crayon-plain-tag">#[IN]:

#各サンプル毎の平均を0にするImageDataGeneratorクラスを作成
samplewise_datagen = ImageDataGenerator(samplewise_center = True)
featurewise_datagen.fit(img_cake, seed = 0)
for i, data in enumerate(samplewise_datagen.flow(img_cake, batch_size = 1)):
  print(np.mean(data[0]))
  if i == 0:
    break</pre>
<pre class="crayon-plain-tag">#[OUT]:

-2.4617513e-05</pre>
<h4>データセット全体の標準偏差を1にする（featurewise_std_normalization）</h4>
<p>次はfeaturewise_std_normalizationです。featurewise_std_normalizationは与えられたデータセット全体の入力の標準偏差を1にします。この時、featurewise_centerはTrueにしなければなりません。出力を確認すると大体ですが、平均がほぼ0、標準偏差がほぼ1になっていることが確認できます。</p>
<div>
<ol>
<li>bool型（True）の場合→データセット全体の標準偏差を1にする</li>
</ol>
</div>
<pre class="crayon-plain-tag">#[IN]:

#データセット全体の平均を0、標準偏差を1にするImageDataGeneratorクラスを作成
featurewise_std_datagen = ImageDataGenerator(featurewise_center = True, featurewise_std_normalization = True)
featurewise_std_datagen.fit(img_cake,seed = 0)
for i, data in enumerate(featurewise_std_datagen.flow(img_cake, batch_size = 1)):
  print(np.mean(data[0]))
  print(np.std(data[0]))
  if i == 0:
    break</pre>
<pre class="crayon-plain-tag">#[OUT]:

-0.00016482393
1.0000116</pre>
<h4>各サンプルの標準偏差を1にする（samplewise_std_normalization）</h4>
<p>次はsamplewise_std_normalizationです。samplewise_std_normalizationは与えられた各サンプルの入力の標準偏差を1にします。用途としてはfeaturewise_std_normalizationとほぼ同じですが、他の条件やモデルに合わせて良い方を使用します。出力を確認すると大体ですが、平均が0、標準偏差が1になっていることが確認できます。</p>
<ol>
<li>bool型（True）の場合→各サンプルの標準偏差を1にする</li>
</ol>
<pre class="crayon-plain-tag">#[IN]:

#各サンプル毎の平均を0、標準偏差を1にするImageDataGeneratorクラスを作成
samplewise_std_datagen = ImageDataGenerator(samplewise_center = True, samplewise_std_normalization = True)
samplewise_std_datagen.fit(img_cake,seed = 0)
for i, data in enumerate(samplewise_std_datagen.flow(img_cake, batch_size=1)):
  print(np.mean(data[0]))
  print(np.std(data[0]))
  if i == 0:
    break</pre>
<pre class="crayon-plain-tag">#[OUT]:

-7.589658e-08
1.0000001</pre>
<h4>関数を使用した前処理（preprocessing_function）</h4>
<p>preprocessing_functionは他の各変換が行われる前に適用される関数を指定できる引数です。ImageDataGeneratorだけでもData Augmentationの種類としては十分ですが、それ以外に自作で変換関数などをImageDataGenerator内で適用させることができます。下記の例では直感的に理解できるよう各画素値に127の値を入れています。RGBが全て127であれば灰色の画像が表示されます。自分で画像の変換関数を作りたい方は使ってみてください。</p><pre class="crayon-plain-tag">#[IN]:

#前処理関数preを用意
def pre(p):
  p = 127
  return p
  
#画像を全て灰色にするImageDataGeneratorクラスを作成
preprocessing_datagen = ImageDataGenerator(preprocessing_function = pre)
show(preprocessing_datagen, img_cake)</pre><p>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-03-16.06.20.png" alt="" /></p>
<h4>複数の変換の適用</h4>
<p>ここまで、各変換を1つずつ適用させてきました。実際のデータセットに対しては複数の変換を適用させることになるので、そちらも確認しておきたいと思います。今回はわかりやすいよう、回転と左右平行移動と拡大縮小の3つの変換を組み合わせたいと思います。下記の画像からどんな画像が生成されているか確認してください。適用した変換は以下になります。</p>
<ol>
<li>「-30度から30度」の範囲でランダムに回転</li>
<li>「640×-0.3〜640×0.3」の範囲でランダムに水平平行移動</li>
<li>「0.7〜1.3」の範囲でランダムにズーム</li>
</ol>
<pre class="crayon-plain-tag">#[IN]:

#複数の変換を適用するImageDataGeneratorクラスを作成
double_datagen = ImageDataGenerator(rotation_range = 30, width_shift_range = 0.3, zoom_range = 0.3)
show(double_datagen, img_cake)</pre>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-03-16.06.30.png" alt="" /><br />
3種類の変換を組み合わせると6枚の画像は全て違う画像となりました。このように複数の変換を組み合わせれば、それだけ画像のバリエーションも増やすことができます。</p>
<p>ここまでImageDataGeneratorクラスによるData Augmentationは一通り解説が終わりました。様々な画像の変換を行ってきましたが、最も重要なのは「目的のために質の良いData Augmentationを行うこと」です。常にテストデータを想定しながら、どのように画像を増やしたら精度が高まるかを考えることが必要です。</p>
<h3>ニューラルネットワークによるCIFAR-10の分類</h3>
<p>ここからは実際のデータセットに対してData Augmentationを実装していきます。特に、Data Augmentationを行う場合と行わない場合について比較していきます。実装コードに関してはここから実行しても問題ありません。しかし、ランタイムについては「GPU」が使用されているかどうか確認してください。前節で切り替えている方はそのままで問題ありません。本節は以下の流れで進行します。</p>
<ol>
<li>データセットの読み込みと表示</li>
<li>データの前処理</li>
<li>モデル構築</li>
<li>学習１（Data Augmentationなし）</li>
<li>評価１</li>
<li>学習２（Data Augmentationあり）</li>
<li>評価２</li>
</ol>
<p>上記の流れの中では、「3.データの前処理」にData Augmentationのコードを記載するのが一般的です。しかし、それではData Augmentationの比較をしにくくなってしまうため、本稿では「5.学習１（Data Augmentationなし）」「7.学習２（Data Augmentationあり）」にそれぞれ必要コードを記載しています。</p>
<h4>1.必要なライブラリのインポート</h4>
<p>まずは必要なライブラリをインポートします。ライブラリの数が非常に多く感じるかもしれませんが、どれも必須なものばかりです。kerasでニューラルネットを構築する際には、大体これくらいのライブラリは必要になりますので、慣れておきましょう。</p><pre class="crayon-plain-tag">#[IN]:

#必要なライブラリのインポート
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

from keras.models import Model
from keras.utils import np_utils
from keras.datasets import cifar10
from keras.preprocessing.image import ImageDataGenerator
from keras.layers import Input
from keras.layers.pooling import MaxPool2D
from keras.layers.convolutional import Conv2D
from keras.layers.core import Dense, Flatten

import tensorflow as tf
tf.random.set_seed(0)</pre><p>
<h4>2.データセットの読み込みと表示</h4>
<p>次にデータセットをロードします。今回使用するデータはCIFAR-10です。CIFAR10は10種類の物体のカラー写真からなるデータセットです。全体で学習用データが50000枚、テスト用データが10000枚です。10種類のラベルの詳細に関しては以下に記載します。CIFAR-10よりもラベル数が多いCIFAR-100も存在します。興味のある方は以下のサイトから調べてみてください。それではkerasのload_dataメソッドを用いてCIFAR-10をロードします。（参考：<a href="https://www.cs.toronto.edu/~kriz/cifar.html" target="_blank" rel="noopener">THE CIFAR-10/CIFAR-100</a>）</p>
<p>CIFAR-10のラベルリスト</p>
<ul>
<li>「０」→飛行機（airplane）</li>
<li>「１」→自動車（automobile）</li>
<li>「２」→鳥（bird）</li>
<li>「３」→猫（cat）</li>
<li>「４」→鹿（deer）</li>
<li>「５」→犬（dog）</li>
<li>「６」→カエル（flog）</li>
<li>「７」→馬（horse）</li>
<li>「８」→船（ship）</li>
<li>「９」→トラック（truck）</li>
</ul>
<pre class="crayon-plain-tag">#[IN]:

#cifar10をダウンロード
(x_train, y_train),(x_test, y_test) = cifar10.load_data()</pre>
<pre class="crayon-plain-tag">#[OUT]:

Downloading data from https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
170500096/170498071 [==============================] - 11s 0us/step
170508288/170498071 [==============================] - 11s 0us/step</pre>
ロードが完了しました。まずはCIFAR-10がどのようなデータセットなのか把握したいと思います。これはこのデータセットに限った話ではありません。どのようなデータセットを使用する場合にも、必ず最初にEDA（探索的データ分析）を行うようにしましょう。AIマガジンでも過去に<a href="https://www.codexa.net/basic-exploratory-data-analysis-with-python/" target="_blank" rel="noopener">【データサイエンティスト入門編】探索的データ解析（EDA）の基礎操作をPythonを使ってやってみよう</a>の記事でEDAに関する解説を行っています。本稿では画像を扱いますので、まずは画像を表示させるところから始めたいと思います。1枚ずつ確認するのは大変なので、30枚をいっぺんに表示させます。<br />
<pre class="crayon-plain-tag">#[IN]:

#ラベルの設定
labels = np.array([
    'airplane',  #飛行機
    'automobile',#バイク
    'bird',      #鳥
    'cat',       #猫
    'deer',      #鹿
    'dog',       #犬
    'frog',      #カエル
    'horse',     #馬
    'ship',      #船
    'truck'      #トラック
    ])</pre>
<pre class="crayon-plain-tag">#[IN]:

#画像の表示のための関数
def image_show(x, y, labels):
  plt.figure(figsize = (13, 10))
  for i in range(30):
    plt.subplot(5, 6, i+1)
    #軸を表示しない
    plt.xticks(color = "None")
    plt.yticks(color = "None")
    plt.tick_params(bottom = False, left = False)
    #タイトルをラベルの名前で表示
    plt.title(labels[y[i][0]])
    #表示
    plt.imshow(x[i])
  return

#画像を表示
image_show(x_train, y_train, labels)</pre>
<img src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-03-16.53.41.jpg" alt="" /><br />
ぱっと見で判断できるものから、一見しただけではわかりにくいものまで様々です。しかし、データセット内にどういう画像が含まれているかは確認できたと思います。このように画像の可視化を行うことはとても重要です。本稿では30枚程度の表示しか行いませんが、例えば画像のラベルごとに表示を行ったり、ラベルの割合を調べたりすることも大切です。この画像は後ほど、Data Augmentationを考える際にも使用します。</p>
<h4>3.データの前処理</h4>
<p>ラベルはバイナリクラスに変更します。<a href="https://www.codexa.net/cnn-mnist-keras-beginner/" target="_blank" rel="noopener">こちら</a>でも述べられていますが、この処理はyの値を10個の数値の配列に変換しています。簡単な図を以下に記載しましたのでイメージをつかんでください。</p>
<ul>
<li>y = 5 = [0,0,0,0,1,0,0,0,0,0]</li>
<li>y = 0 = [1,0,0,0,0,0,0,0,0,0]</li>
</ul>
<pre class="crayon-plain-tag">#[IN]:

#ラベルをバイナリクラスにする
categorical_y_train = np_utils.to_categorical(y_train, 10)
categorical_y_test = np_utils.to_categorical(y_test, 10)
print(categorical_y_train[0])</pre>
<pre class="crayon-plain-tag">#[OUT]:

[0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]</pre>
<h4>4.モデル構築</h4>
<p>ここからモデル構築に移ります。今回のモデルは畳み込み（Conv）層が4つ、プーリング（Pooling）層が3つ、全結合（Dense）層が2つです。畳み込み層のカーネルサイズを3×3、画像サイズが変わらないよう（&#8221;same&#8221;）に設定し、活性化関数にはReLU関数を用いています。またプーリング層にはMaxPoolingを使用しています。全結合層は最終的に出力が10で活性化関数をsoftmaxにしています。</p>
<p>以下の内容は本稿で記載すると収まらないため、深く解説しません。しかし、ニューラルネットワークを構築する上では必須となる内容ですので、ご自身で補うことをお勧めします。ここでは、一旦学習の進み具合にのみ注目してみます。</p>
<ul>
<li>損失関数（loss function）</li>
<li>最適化（optimizer）</li>
<li>確率的最急降下法(sgt）</li>
<li>評価関数（metrics）</li>
</ul>
<pre class="crayon-plain-tag">#[IN]:

#モデルを構築
inp=Input(shape = (32, 32, 3))
x = Conv2D(64, (3, 3), padding = 'same', activation = "relu", use_bias = True)(inp)
x = MaxPool2D(pool_size = (2, 2))(x)
x = Conv2D(128, (3, 3), padding = 'same', activation = "relu", use_bias = True)(x)
x = MaxPool2D(pool_size = (2, 2))(x)
x = Conv2D(256, (3, 3), padding = 'same', activation = "relu", use_bias = True)(x)
x = MaxPool2D(pool_size=(2,2))(x)
x = Conv2D(512, (3, 3), padding = 'same', activation = "relu", use_bias = True)(x)
x = Flatten()(x)
x = Dense(512, activation = 'relu', use_bias = True)(x)
out = Dense(10, activation = 'softmax', use_bias = True)(x)</pre>
本稿でのモデルは、Data Augmentationを行わない時と行う時の2種類を作成します。どちらもモデルの条件は同じにします。<br />
<pre class="crayon-plain-tag">#[IN]:

#Data Augmentationを行わない用
normal_model = Model(inputs = inp, outputs = out)
normal_model.compile(optimizer = 'adam', loss = 'categorical_crossentropy', metrics = ['accuracy'])</pre>
<pre class="crayon-plain-tag">#[IN]:

#Data Augmentationを行う用
augmentation_model = Model(inputs = inp, outputs = out)
augmentation_model.compile(optimizer = 'adam',loss = 'categorical_crossentropy', metrics = ['accuracy'])</pre>
<h4>5.学習１（Data Augmentationなし）</h4>
<p>モデルの構築が完了しましたので、Data Augmentationがない場合の学習を行いたいと思います。Data Augmentationは行いませんが、ニューラルネットワークを使用するため、画像のピクセルを0〜1の間に収めます。そのため、ImageDataGeneratorクラスのスケーリングだけは適用します。テストデータも同様にスケーリングのみを適用します。その後、flowメソッドを使用してデータのバッチを生成します。</p><pre class="crayon-plain-tag">#[IN]:

#学習用のImageDataGeneratorクラスの作成
normal_train_datagen = ImageDataGenerator(rescale = 1./255)
#学習用のバッチの生成
normal_train_generator = normal_train_datagen.flow(x_train, categorical_y_train, batch_size = 32, seed = 0)
#テスト用のImageDataGeneratorクラスの作成
test_datagen = ImageDataGenerator(rescale = 1./255)
#テスト用のバッチの生成
test_generator = test_datagen.flow(x_test, categorical_y_test, batch_size = 32, seed=0)</pre><p>
&nbsp;</p>
<p>学習時の条件ですが、バッチサイズを32、エポック数を20 にしています。これらは「7.学習２（Data Augmentationあり）」でも同様の値を使用します。これらの値については決められた値というのは存在しません。経験則やデータセットと相談しながら探っていくことになります。</p>
<blockquote><p>本稿では、GPU上の乱数に関してはシード値を固定していません。そのため、下記出力と若干異なる出力が表示されると思いますが、基本的には問題ありません。GPUの再現性に興味がある方は別途調べていただくようお願いいたします。</p></blockquote>
<pre class="crayon-plain-tag">#[IN]:

#学習
normal_result = normal_model.fit(normal_train_generator,steps_per_epoch=len(x_train) / 32, epochs=20)</pre>
<pre class="crayon-plain-tag">#[OUT]:

Epoch 1/20
1562/1562 [==============================] - 69s 17ms/step - loss: 1.6774 - accuracy: 0.3698
Epoch 2/20
1562/1562 [==============================] - 26s 16ms/step - loss: 0.9568 - accuracy: 0.6592
Epoch 3/20
1562/1562 [==============================] - 26s 16ms/step - loss: 0.7133 - accuracy: 0.7499
Epoch 4/20
1562/1562 [==============================] - 26s 16ms/step - loss: 0.5719 - accuracy: 0.7970
Epoch 5/20
1562/1562 [==============================] - 26s 16ms/step - loss: 0.4447 - accuracy: 0.8434
Epoch 6/20
1562/1562 [==============================] - 26s 16ms/step - loss: 0.3296 - accuracy: 0.8836
Epoch 7/20
1562/1562 [==============================] - 26s 16ms/step - loss: 0.2413 - accuracy: 0.9157
Epoch 8/20
1562/1562 [==============================] - 26s 16ms/step - loss: 0.1898 - accuracy: 0.9334
Epoch 9/20
1562/1562 [==============================] - 26s 16ms/step - loss: 0.1557 - accuracy: 0.9459
Epoch 10/20
1562/1562 [==============================] - 26s 16ms/step - loss: 0.1303 - accuracy: 0.9546
Epoch 11/20
1562/1562 [==============================] - 26s 17ms/step - loss: 0.1130 - accuracy: 0.9618
Epoch 12/20
1562/1562 [==============================] - 26s 17ms/step - loss: 0.1118 - accuracy: 0.9620
Epoch 13/20
1562/1562 [==============================] - 26s 17ms/step - loss: 0.1030 - accuracy: 0.9658
Epoch 14/20
1562/1562 [==============================] - 26s 17ms/step - loss: 0.1103 - accuracy: 0.9630
Epoch 15/20
1562/1562 [==============================] - 26s 17ms/step - loss: 0.0949 - accuracy: 0.9687
Epoch 16/20
1562/1562 [==============================] - 26s 17ms/step - loss: 0.0966 - accuracy: 0.9694
Epoch 17/20
1562/1562 [==============================] - 26s 17ms/step - loss: 0.0983 - accuracy: 0.9678
Epoch 18/20
1562/1562 [==============================] - 26s 17ms/step - loss: 0.0977 - accuracy: 0.9692
Epoch 19/20
1562/1562 [==============================] - 26s 17ms/step - loss: 0.0877 - accuracy: 0.9727
Epoch 20/20
1562/1562 [==============================] - 26s 17ms/step - loss: 0.1009 - accuracy: 0.9677</pre>
学習が完了しました。accuracyの推移を見るためにグラフでプロットを行います。<br />
<pre class="crayon-plain-tag">#[IN]:

#accuracyのプロット
plt.plot(range(1, 21), normal_result.history['accuracy'], label = "train")
#軸ラベル名
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
#表示
plt.legend()
plt.show()</pre>
<img src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-07-11.14.28.png" alt="" /></p>
<h4>6.評価１</h4>
<p>学習データのaccuracyは0.9を上回るほど良い結果を残しています。これは、かなり正確に分類できていると言えます。これと同等の精度がテストデータでも得られれば問題ありません。それではテストデータで評価を行います。評価にはevaluateメソッドを使用し、先ほど作成したtest_generatorを引数に渡してあげます。学習用データとのaccuracyとの差に注目してください。</p><pre class="crayon-plain-tag">#[IN]:

#テスト用データを使って評価
normal_evaluate = normal_model.evaluate(test_generator)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

313/313 [==============================] - 3s 8ms/step - loss: 1.7661 - accuracy: 0.7427</pre><p>
学習用データの方のaccuracyは0.9を上回っていたのに対し、テストデータのaccuracyは0.74程度です。これは今回のモデルが過学習を起こしていることを意味します。モデルが学習用データに過度に適合したことにより、テストデータに対する評価が下がってしまっています。つまりData Augmentationを行わないことによって発生しうる事象の一例です。そのため、次はData Augmentationを用いて、この結果を少しでも改善させられるようにしていきます。</p>
<h4>7.学習２（Data Augmentationあり）</h4>
<p>まずはどのようなData Augmentationが有効かどうか考えて見たいと思います。先ほど表示した30枚の画像と同様の画像を表示して考えます。そして表示した画像から考えられる以下の点を仮説として挙げます。</p><pre class="crayon-plain-tag">#[IN]:

#画像の表示
image_show(x_train, y_train, labels)</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-03-16.53.41.jpg" alt="" /></p>
<ul>
<li>撮影時の向きはバラバラなので少し回転を加えた方が良さそうである。</li>
<li>動物の体の向きは左右バラバラなので左右反転をした方が良さそうである。</li>
<li>フレーム内の動物の位置はズレているので少し、上下左右にシフトした方が良さそうである。</li>
<li>動物の大きさはバラバラなので少しズームをした方が良さそうである。</li>
<li>撮影時の明るさがバラバラなので少し色を変えた方が良さそうである。</li>
</ul>
<p>以上の仮説をData Augmentationとして適用させてみます。「5.学習１」と同様にImageDataGeneratorクラスで変換を定義し、flowメソッドを使用してデータのバッチを生成します。</p><pre class="crayon-plain-tag">#[IN]:

#学習用のImageDataGeneratorクラスの作成
augmentation_train_datagen = ImageDataGenerator(
    #回転
    rotation_range = 10,
    #左右反転
    horizontal_flip = True,
    #上下平行移動
    height_shift_range = 0.2,
    #左右平行移動
    width_shift_range = 0.2,
    #ランダムにズーム
    zoom_range = 0.2,
    #チャンネルシフト
    channel_shift_range = 0.2,
    #スケーリング
    rescale = 1./255
    )
#学習用のバッチの生成
augmentation_train_generator = augmentation_train_datagen.flow(x_train, categorical_y_train, batch_size=32, seed=0)</pre><p>
学習時の条件も「5.学習１」と同様で、バッチサイズを32、エポック数を20 にしています。</p><pre class="crayon-plain-tag">#[IN]:

#学習
augmentation_result = augmentation_model.fit(augmentation_train_generator, steps_per_epoch = len(x_train) / 32, epochs = 20)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

Epoch 1/20
1562/1562 [==============================] - 59s 37ms/step - loss: 1.2971 - accuracy: 0.5997
Epoch 2/20
1562/1562 [==============================] - 58s 37ms/step - loss: 0.9787 - accuracy: 0.6654
Epoch 3/20
1562/1562 [==============================] - 58s 37ms/step - loss: 0.9140 - accuracy: 0.6839
Epoch 4/20
1562/1562 [==============================] - 58s 37ms/step - loss: 0.8811 - accuracy: 0.6970
Epoch 5/20
1562/1562 [==============================] - 58s 37ms/step - loss: 0.8456 - accuracy: 0.7096
Epoch 6/20
1562/1562 [==============================] - 58s 37ms/step - loss: 0.8162 - accuracy: 0.7179
Epoch 7/20
1562/1562 [==============================] - 58s 37ms/step - loss: 0.7918 - accuracy: 0.7260
Epoch 8/20
1562/1562 [==============================] - 58s 37ms/step - loss: 0.7816 - accuracy: 0.7327
Epoch 9/20
1562/1562 [==============================] - 58s 37ms/step - loss: 0.7658 - accuracy: 0.7359
Epoch 10/20
1562/1562 [==============================] - 58s 37ms/step - loss: 0.7422 - accuracy: 0.7423
Epoch 11/20
1562/1562 [==============================] - 58s 37ms/step - loss: 0.7354 - accuracy: 0.7444
Epoch 12/20
1562/1562 [==============================] - 58s 37ms/step - loss: 0.7127 - accuracy: 0.7542
Epoch 13/20
1562/1562 [==============================] - 58s 37ms/step - loss: 0.7111 - accuracy: 0.7512
Epoch 14/20
1562/1562 [==============================] - 58s 37ms/step - loss: 0.6979 - accuracy: 0.7580
Epoch 15/20
1562/1562 [==============================] - 58s 37ms/step - loss: 0.7035 - accuracy: 0.7549
Epoch 16/20
1562/1562 [==============================] - 57s 37ms/step - loss: 0.6813 - accuracy: 0.7645
Epoch 17/20
1562/1562 [==============================] - 57s 36ms/step - loss: 0.6806 - accuracy: 0.7657
Epoch 18/20
1562/1562 [==============================] - 57s 37ms/step - loss: 0.6598 - accuracy: 0.7710
Epoch 19/20
1562/1562 [==============================] - 57s 36ms/step - loss: 0.6572 - accuracy: 0.7722
Epoch 20/20
1562/1562 [==============================] - 57s 36ms/step - loss: 0.6645 - accuracy: 0.7701</pre><p>
学習が完了しました。accuracyの推移を見るためにグラフでプロットを行います。</p><pre class="crayon-plain-tag">#[IN]:

#accuracyのプロット
plt.plot(range(1, 21), augmentation_result.history['accuracy'], label = "train")
#軸ラベル名
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
#表示
plt.legend()
plt.show()</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/06/スクリーンショット-2021-06-07-11.24.04.png" alt="" /></p>
<h4>8.評価２</h4>
<p>学習データのaccuracyは0.78程度です。これは、先ほどに比べると良い評価を得られていません。この評価を踏まえた上で、テストデータで評価を行います。評価にはevaluateメソッドを使用し、先ほど作成したaugmentation_test_generatorを引数に渡してあげます。学習用データとのaccuracyとの差に注目してください。</p><pre class="crayon-plain-tag">#[IN]:

#テスト用データを使って評価
augmentation_evaluate = augmentation_model.evaluate(test_generator)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

313/313 [==============================] - 3s 8ms/step - loss: 0.6431 - accuracy: 0.7857</pre><p>
テストデータのaccuracyも0.78程度になっています。つまり結果的にData Augmentationを行なった方が、行わない場合に比べて評価の高いモデルを作成できたことになります。この要因の一つにData Augmentationによって過学習を抑制できたことが挙げられます。Data Augmentationによって学習用データの画像が水増しされたことにより、モデルは本来用意されている画像数よりも多くの画像を学習に使用しました。それにより、学習用データの難易度も上がりましたが、テストデータに対する適合率も上がりました。結果、テストデータのaccuracyはData Augmentationを行わない場合に比べて上がったと考えられます。</p>
<p>accuracyが上がったことで今回適用したData Augmentationの手法が有効であったと考えられます。このように画像を観察した上で特徴を探りData Augmentationを適用することはどのデータセットでも重要です。本稿を読まれている方々には、是非本稿のコードを利用して様々なData Augmentationの手法を試していただきたいと思います。</p>
<h3>まとめ</h3>
<p>本稿ではData Augmentationの必要性から実装まで解説しました。画像データは容量も大きいため扱いが難しいですが、視覚的に確認できるため、機械学習の中でも非常に楽しい分野だと思います。本稿が少しでも機械学習を勉強したいと思われている方々の参考になれば幸いです。</p>
<p>CodexaのコースやAIマガジンでは画像認識の内容を含んだものが他にも存在します。興味がある方はそちらも確認してみてください。皆様の受講をお待ちしております。</p>
<p><a href="https://www.codexa.net/keras-cnn-python/" target="_blank" rel="noopener">機械学習 チュートリアル　はじめての画像認識</a><br />
<a href="https://www.codexa.net/opencv_python_introduction/" target="_blank" rel="noopener">【AIマガジン】OpenCV 入門：画像処理・画像認識・機械学習の実装を徹底解説（全実装コード公開）</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.codexa.net/data_augmentation_python_keras/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>正規化・標準化を徹底解説 （Python 前処理 サンプルコード付き）</title>
		<link>https://www.codexa.net/normalization-python/</link>
					<comments>https://www.codexa.net/normalization-python/#respond</comments>
		
		<dc:creator><![CDATA[codexaチーム]]></dc:creator>
		<pubDate>Wed, 07 Jul 2021 05:00:19 +0000</pubDate>
				<category><![CDATA[機械学習]]></category>
		<guid isPermaLink="false">https://www.codexa.net/?p=2987</guid>

					<description><![CDATA[私たちは普段から様々なものを「比較」しています。例えばテストの点数や売り上げなどは、他人の点数や他店舗の売り上げと比較して優劣が決まります。このような時、同じ単位での比較はさほど難しくはありません。しかし、単位が違う時は [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>私たちは普段から様々なものを「比較」しています。例えばテストの点数や売り上げなどは、他人の点数や他店舗の売り上げと比較して優劣が決まります。このような時、同じ単位での比較はさほど難しくはありません。しかし、単位が違う時はどうでしょう。平熱と身長を例に挙げてみます。平熱が37.3度の人は「他人と比較して平熱が高い」と言えます。身長が200cmの人は「他人と比較して身長が高い」と言えます。それでは、平熱と身長を比較した時、優劣や大小をつけるとしたらどちらが上なのでしょうか。</p>
<p>多くの特徴量を扱う機械学習では、元の数値のままでは比較が難しい場合が存在します。近年のAIブームにより、機械学習の技術の凄さが報道されていますが、全てにおいて万能ではありません。その能力を最大限発揮するためにはデータに対する前処理が必要になってきます。</p>
<p>本稿では、機械学習の前処理の1つである「正規化」と「標準化」について解説していきます。前半は言葉の意味からライブラリの実装方法まで。後半では実際のデータセットを用いて、正規化や標準化が結果に対してどのような影響を与えるかを解説します。本稿が機械学習をこれから学びたいと思っている方や、初学者の方にとっての参考になれば幸いです。</p>
<h3>スケーリングとは</h3>
<p>正規化や標準化の内容に入る前に、「スケーリング」という言葉について解説します。スケーリング（Feature Scaling）とは特徴量において値を一定のルールに基づいて一定の範囲に変換する処理のことを指します。本稿では、このスケーリングに含まれる以下の2つの手法について解説していきます。</p>
<ul>
<li>正規化（Normalization）</li>
<li>標準化（Standardization）</li>
</ul>
<p><img class="alignnone" src="https://www.codexa.net/wp-content/uploads/2021/05/1.png" alt="" /></p>
<p>詳細・実装に関しては後述しますが、図1のように本稿での正規化は「特徴量を0〜1に変換するスケーリング」、標準化は「特徴量の平均を0、分散を1に変換するスケーリング」と定義することを先に記載しておきます。これらの言葉の意味は記事や分野によってニュアンスが異なる場合があります。本稿では図1のように定義していますが、絶対ではありません。特に正規化に関しては分野によって言葉の意味が違います。他分野の正規化の意味を調べたい方は、Wikipediaにも分野別に記載されていますので、確認してみてください。（参考：<a href="https://ja.wikipedia.org/wiki/%E6%AD%A3%E8%A6%8F%E5%8C%96">WIkipedia 正規化</a>）</p>
<p>こちらも詳細は後述しますが、スケーリングは全ての機械学習モデルに有効に働くわけではないことに注意してください。そのため、ここからの解説はスケーリングが比較的有効である場合のモデルを前提に進めていきます。</p>
<h4>スケーリングの目的①</h4>
<p>スケーリングの目的の1つは特徴量がもつ値の重みを平等にするということです。これらは<a href="https://www.codexa.net/support-vector-machine-tutorial/">SVM（Support Vector Machine）</a>や<a href="https://www.codexa.net/collaborative-filtering-k-nearest-neighbor/">kNN（k-Nearest Neighbors）</a>などの特徴量間の距離をベースとしたモデルに有効とされています。機械学習を扱うにおいて特徴量は基本的に数値が使用されます。数値には我々が普段扱うような単位の情報はありません。そのため、モデルは学習時に単位の違う数値同士を同じ尺度で捉えてしまう可能性があります。これを防ぐためにスケーリングは存在します。言葉だけで考えると難しいと感じる方もいらっしゃると思いますので、この後の節では表を用いながらスケーリングを「適応しない場合」と「適応する場合」の比較をしていきます。</p>
<h5>スケーリングを適応しない場合</h5>
<p>まずは適応しない場合について考えます。表Aには生徒の身体的特徴から機械学習を用いて生徒の体調を予測するときに用意されたデータセットを示しています。こちらはスケーリングを適応する場合でも使用します。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/05/図2.png" alt="" /></p>
<p>表Aを見てください。生徒ごとの身長と平熱が記載されています。身長と平熱の特徴量を比較してどちらの方が重みが大きいと思うでしょうか。身長は162cm〜182cmの間に収まっていて、平熱は35.6℃〜37.1℃の間に収まっています。どちらも一般的な範囲だと判断できると思います。今回は、この2つの特徴量について比較するためにユークリッド距離を用いて生徒同士の距離を計算してみたいと思います。ユークリッド距離についての詳細な説明は省きますが、基本的には点と点を定規で測った場合の距離と考えていただければ大丈夫です。（参考：<a href="https://ja.wikipedia.org/wiki/%E3%83%A6%E3%83%BC%E3%82%AF%E3%83%AA%E3%83%83%E3%83%89%E8%B7%9D%E9%9B%A2">WIkipedia：ユークリッド距離</a>）</p>
<p>今回は生徒A-C間、生徒D-E間についてのユークリッド距離を求めます。比較のため、A-C間は身長を固定し、A-D間は平熱を固定します。</p>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/05/図3.png" alt="" /></p>
<p>A-C間とD-E間のユークリッド距離を見てみると、D-E間のユークリッド距離の方が明らかに大きくなっています。この理由は計算式を見れば明らかです。平熱の数値の幅に比べて身長の数値の幅の方が広くなっているためです。しかし、2つのユークリッド距離には違和感が生じます。平熱での1.5℃の差に対するユークリッド距離が、身長の20cmの差に比べて近すぎます。これは筆者の感覚ですが、平熱が1.5℃違う人を2人探し出すことは、身長が20cm違う2人を探し出すのと同じくらいか、もしくはより難しいと感じます。身長と平熱の値がもつ範囲が一定でないことにより、明らかに身長に対する重みが大きくなっています。このように本来単位が違うはずの特徴量を、同じ尺度で捉えようとすると比較が難しくなる場合があります。機械学習モデルも同様に、学習がうまくいかなくなる可能性があります。</p>
<h5>スケーリングを適応した場合</h5>
<p>次にスケーリングを適応した場合を考えます。表Bは表Aに対して、標準化のスケーリングを適応した場合の表です。表Bの値は少数第3位まで求めています。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/05/図4.png" alt="" /></p>
<p>表Bを見ると、標準化によって表Aがスケーリングされています。記載した通り、標準化は各特徴量の平均を0、標準偏差が1になるような分布に変換しています。そのため、同じ特徴量内で元の値が同じであれば、標準化後の値も同じになります。そのため、身長のAとBは「-0.304」、平熱のDとEは「-0.287」で等しいことが表Bから読み取れます。スケーリングを適応していない場合と同様にユークリッド距離を用いて、生徒間の距離を計算します。計算結果を見比べてみましょう。</p>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/05/図5.png" alt="" /></p>
<p>A-C間とD-E間のユークリッド距離を見てみると、標準化のスケーリングを行わない場合と比較して、ユークリッド距離の差が小さくなっていることが分かります。この数値であれば、身長の20cm差と平熱の1.5℃の比較について、スケーリング前よりも比較的正確に行うことができます。今回は身長と平熱で比較しましたが、さらに数値の差が大きくなるような特徴量同士ではこの問題はより顕著になります。</p>
<h4>スケーリングの目的②</h4>
<p>スケーリングを行うもう一つの目的は、学習のコストの削減です。これは線形回帰やニューラルネットワークや最急降下法を用いたモデルで有効とされています。特徴量の範囲が異なれば、最急降下時に更新されるパラメーターのサイズが異なってしまします。学習がスムーズに行われるために、スケーリングが必要なのです。最急降下法に関しては数式的な理解が必要になるため、ここでは説明を省きます。</p>
<p>線形回帰や最急降下法についてより詳しく知りたい方はcodexaが提供している下記のコースを参照すると理解が深まります。こちらも避けては通れない内容ですので、理解しておきましょう。本稿に対する理解度もより深まると思います。</p>
<ul>
<li><a href="https://www.codexa.net/linear-regression-for-beginner/">機械学習線形回帰 入門</a></li>
<li><a href="https://www.codexa.net/tutorial-linear-regression-for-beginner/">機械学習 チュートリアル線形回帰</a></li>
</ul>
<h4>スケーリングの影響が少ないモデル</h4>
<p>スケーリングが有効なモデルが存在するのに対して、比較的影響を受けにくいモデルも存在します。ランダムフォレストや、LightGBMなどのツリーベース型のモデルです。ツーリーベース型のモデルは各特徴量内の分割を繰り返します。そのため、特徴量同士のスケールを合わせなくても、影響を受けにくくなります。ツリーベース型のモデルの詳細については非常に深い内容となってしまうため、本稿では解説しません。codexaが提供するコースやAIマガジンを参考にしてみてください。</p>
<ul>
<li><a href="https://www.codexa.net/decision-tree-random-forest/">機械学習 チュートリアル決定木とランダムフォレスト</a></li>
<li><a href="https://www.codexa.net/lightgbm-beginner/">【AIマガジン】LightGBM 徹底入門 – LightGBMの使い方や仕組み、XGBoostとの違いについて</a></li>
</ul>
<h3>「正規化」と「標準化」の定義と実装</h3>
<p>ここまでスケーリングの目的、適応するモデルについて解説してきました。ここからは実際にスケーリング手法の正規化と標準化について、それぞれを解説→実装の順番で記載していきます。後半で実際のデータセットに適応させる際にスムーズに進められるよう、コーディングとライブラリの仕様を詳細に解説します。</p>
<p>本稿はGoogle Colabを用いて実装していきます。2021年7月時点でコードの実行確認を行いましたので、Google Colabのデフォルトのバージョンが変更されない限り、ライブラリをそのままインポートすれば同じように実装可能です。是非、ご自身でも実装してみてください。Google Colabを使用したことがない方は下記の記事を参考にしてください。（参考：<a href="https://www.codexa.net/how-to-use-google-colaboratory/">Google Colabの知っておくべき使い方 – Google Colaboratoryのメリット・デメリットや基本操作のまとめ</a>）</p>
<h4>正規化とは</h4>
<p>まずは、正規化（Normalization）について解説していきます。実際には「Normalization」は複数の手法を持ち、本稿で述べている正規化は英語で「min-max normalization」と表記されます。スケーリングの解説時に記載したように、正規化とは各特徴量に対して0〜1に変化する処理のことを指します。各特徴量の取りうる範囲が違ったとしても、正規化によって揃えることができます。正規化についての数式を見て見ましょう。</p>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/05/6.png" alt="" /></p>
<p>特徴量内の各値を最小値から引いたものを、特徴量内の最大値から最小値を引いた値で割ることで正規化を行なっています。式自体はシンプルなため、理解自体は難しくないと思います。使用する際の注意点としては、正規化は外れ値に敏感であることです。非常に大きな値や小さな値が外れ値として存在していた場合、他の値が0や1付近に引っ張られます。そのため、正規化を使用する際は外れ値が存在していないかを確認する必要があります。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/05/図7.png" alt="" /></p>
<p>表Dはスケーリングで使用した表Aに対して、外れ値を付与したものです。表Dと表Eは正規化後の値を示しています。表Dを見てみると生徒Cの身長（10000cm）と生徒Dの平熱（0℃）が外れ値として存在しています。その結果、表Eにおいて身長は0付近に、平熱は1付近に値が引っ張られています。こういった現象が発生するため、正規化を扱う際の外れ値の存在には注意が必要です。</p>
<h4>正規化のPython 実装</h4>
<p>ここから正規化の実装に入ります。視覚的に正規化による変換を理解するため、ランダムな乱数を発生させ、正規化を行います。正規化の適応部分以外はそこまで重要な内容ではないので詳細な説明は省きますが、乱数の生成や、可視化方法に興味がある方は参考にしてみてください。正規化の実装は下記の流れに沿って行います。「1.scikit-learnによる実装」だけ正規化前後の点をプロットします。</p>
<p style="padding-left: 40px;">0. 乱数の生成<br />
1. scikit-learnによる正規化の実装（正規化前後のプロットを含む）<br />
2. Numpyによる正規化の実装<br />
3. Pandasによる正規化の実装</p>
<h5>0.乱数の生成</h5>
<p>まずはNumpyを用いて乱数を発生させます。Pandasでの正規化も実装するので、DataFrame型に変換したものも用意しておきます。</p><pre class="crayon-plain-tag">#[IN]:

#ライブラリのインポート
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

#2次元の乱数の生成
np.random.seed(seed = 0)
data_n = np.random.multivariate_normal([4, 4], [[2, 0], [0, 2]], 200 )
#DataFrame型も用意
data_p = pd.DataFrame(data_n)
#numpyとpandasで頭5行を表示
print("numpyの配列↓")
print(data_n[0:5])
print("pandasのDataFrame↓")
print(data_p.head(5))</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

numpyの配列↓
[[6.49474675 4.56590775]
 [5.38414453 7.16910155]
 [6.64112584 2.61792037]
 [5.34362793 3.78594858]
 [3.8540265  4.58067397]]
pandasのDataFrame↓
          0         1
0  6.494747  4.565908
1  5.384145  7.169102
2  6.641126  2.617920
3  5.343628  3.785949
4  3.854026  4.580674</pre><p>
スケーリング前とスケーリング後の点を可視化するための関数を定義します。</p><pre class="crayon-plain-tag">#[IN]:

def image_show(data_beore,data_after,title):
  #出力サイズ
  plt.figure(figsize = (8, 8))
  #タイトル
  plt.title(title)
  #x軸とy軸の範囲を設定
  plt.xlim([-8, 8])
  plt.ylim([-8, 8])
  #目盛の刻み
  plt.xticks(np.arange(-8, 9, 1))
  plt.yticks(np.arange(-8, 9, 1))
  #プロットの設定
  plt.scatter(data_beore[:, 0], data_beore[:, 1], c='blue', s=10, label='before')
  plt.scatter(data_after[:, 0], data_after[:, 1], c='red', s=10, label='after')
  #ラベルの設定
  plt.legend(fontsize = 15)
  #中心ラインの設定
  plt.hlines(0, xmin = -8, xmax = 8, linestyles = 'dashed')
  plt.vlines(0, ymin = -8, ymax = 8, linestyles = 'dashed')
  plt.show()
  return</pre><p>
<h5>1.scikit-learnによる正規化の実装（正規化前後のプロットを含む）</h5>
<p>ここからそれぞれのライブラリ毎に正規化を実装します。最初はscikit-learnのMinMaxScalerクラスを用います。他の手法による正規化の実装と結果を比較するために、正規化後の最初5行を表示します。正規化前後の点も同時にプロットし、正規化前を青、正規化後を赤として表示します。</p><pre class="crayon-plain-tag">#[IN]:

from sklearn.preprocessing import MinMaxScaler

#正規化のクラスを準備
ms = MinMaxScaler()

#特徴量の最大値と最小値を計算し変換
data_sc = ms.fit_transform(data_n)

#プロット
image_show(data_n,data_sc, 'min-max normalization')</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/05/スクリーンショット-2021-05-06-15.32.53.png" alt="" /></p><pre class="crayon-plain-tag">#[IN]:

#最初5行を提示
print(data_sc[0:5])</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

[[0.87722065 0.63085115]
 [0.7214759  0.9968524 ]
 [0.89774804 0.35696997]
 [0.71579408 0.52119123]
 [0.50690057 0.63292724]]</pre><p>
正規化後の点が赤でプロットされました。この出力を見ても、正規化が0〜1にスケーリングしているのが視覚的に分かると思います。正規化の実装を見てみます。MinMaxScalerクラスを定義した後、メソッドを使用して変換を行います。今回はfit_transform()メソッドを使用しましたが、その他のメソッドについての一部も記載しておきます。</p>
<p>fit()　　　　　　　　　 与えられた特徴量の最大値と最小値を計算する。<br />
fit_transform()　　　　 最大値と最小値を計算し、変換する。<br />
inverse_transform()　　正規化を元に戻す。<br />
transform()　　　　　　fit()で得られた値を用いて変換する。<br />
適応されたクラスは属性を持ちます。属性が持つ値についても一部表示します。</p><pre class="crayon-plain-tag">#[IN]:

print("各特徴量ごとの最小値は"+str(ms.data_min_))
print("各特徴量ごとの最大値は"+str(ms.data_max_))
print("各特徴量ごとの範囲は"+str(ms.data_range_))
print("サンプル数は"+str(ms.n_samples_seen_))</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

各特徴量ごとの最小値は[0.23936256 0.07896172]
各特徴量ごとの最大値は[7.37027566 7.19148898]
各特徴量ごとの範囲は[7.13091311 7.11252726]
サンプル数は200</pre><p>
特徴量毎の最小値、最大値、最大値と最小値の幅が表示されました。「1.scikit-learnによる正規化の実装」については以上になります。本稿で解説した部分だけでも十分に扱えるかと思いますが、さらに詳細を知りたい方はscikit-learnの公式ドキュメントを参考にしてみてください。（参考：<a href="https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.MinMaxScaler">sklearn.preprocessing.MinMaxScaler</a>）</p>
<h5>2.Numpyによる正規化の実装</h5>
<p>次にNumpyのメソッドを用いて実装を行なっていきます。乱数に関しては最初に定義したdata_n変数を用います。Numpyのメソッドを用いた実装の場合はscikit-learnとは異なり正規化の計算式を定義してあげる必要があります。今回はmin_max_n()という関数を作成し、正規化後の値を返すようにします。</p><pre class="crayon-plain-tag">#[IN]:

#正規化を行う関数の定義
def min_max_n(n, axis=None):
  #最小値の計算
  min_n = n.min(axis = axis, keepdims = True)
  #最大値の計算
  max_n = n.max(axis = axis, keepdims = True)
  #正規化の計算
  min_max_n = (n - min_n) / (max_n - min_n)
  return min_max_n</pre><p>
</p><pre class="crayon-plain-tag">#[IN]:

#最初5行を表示
print(min_max_n(data_n,axis=0)[0:5])</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

[[0.87722065 0.63085115]
 [0.7214759  0.9968524 ]
 [0.89774804 0.35696997]
 [0.71579408 0.52119123]
 [0.50690057 0.63292724]]</pre><p>
「1.scikit-learnによる正規化の実装」と同じ値が出力されています。実装の容易さという点ではscikit-learnを用いた方が簡単だと思います。しかし、実装の内容を知るという点で一度こちらのコードでも実装し理解することをお勧めします。メソッドを使用して計算すると、内部の実装を理解せずに先に進めてしまいます。非常に便利ですが、学習という観点から見ると悪い点でもありますので、可能な限り理解できるように努めましょう。</p>
<h5>3.Pandasによる正規化の実装</h5>
<p>最後にpandasのメソッドを用いて正規化を実装します。DataFrame型であったとしてもscikit-learnを用いて正規化は実装できます。その際、返り値はNumpyになることに注意してください。「2.Numpyによる正規化の実装」と同様に実装の中身を知るという点でPandasを用いても実装できるようにしておくことをお勧めします。また、Pandasは機械学習を扱う上で必須なライブラリなため、基本的な使い方は押さえておきましょう。min_max_p()という関数を作成し、正規化後の値を返すようにします。</p><pre class="crayon-plain-tag">#[IN]:

#正規化を行う関数の定義
def min_max_p(p):
  #最小値の計算
  min_p = p.min()
  #最大値の計算
  max_p = p.max()
  #正規化の計算
  min_max_p = (p - min_p) / (max_p - min_p)
  return min_max_p</pre><p>
</p><pre class="crayon-plain-tag">#[IN]:

#最初5行を表示
print(min_max_p(data_p).head(5))</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

          0         1
0  0.877221  0.630851
1  0.721476  0.996852
2  0.897748  0.356970
3  0.715794  0.521191
4  0.506901  0.632927</pre><p>
「1.scikit-learnによる正規化の実装」、「2.Numpyによる正規化の実装」と同じ値が出力されています。これで正規化に関する実装は終わりになります。後半でも正規化を使用する場面があるので、分からない部分がある方は数式と実装を見比べながら確認しておいてください。</p>
<h4>標準化とは</h4>
<p>次に、標準化（Standardization）について解説していきます。正規化とは異なり、標準化は基本的に平均を0、標準偏差を1にする変換として定義されています。入力の特徴量が正規分布に従うと仮定した変換を行うことができます。標準化を式で表すと下記のようになります。</p>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/05/図8.png" alt="" /></p>
<p>特徴量内の各値から平均値を引いた値を標準偏差で割ることで標準化を実現します。正規化が0〜1の範囲に収めているのとは異なり、標準化は特定の範囲に収めるような変換ではない点を理解してください。標準化はあくまで、分布内の平均値を0、標準偏差を1にする変換です。</p>
<p>標準化を実装する上で注意する点は、標準偏差（σ）には通常の標準偏差と不偏標準偏差が存在するということです。データには母集団と呼ばれる全体を示すデータと標本と呼ばれる母集団から抽出されたデータがあります。その中で、母集団から求められる分散を母分散、標本から求められる分散を不偏分散と呼びます。この時、統計学的に、不偏分散は母分散よりも少しだけ小さくなることが知られています。そのため、分散の平方根である標準偏差にも若干の違いが生じます。本稿では数式には触れませんが、標準偏差を利用する標準化の実装においてこの2つの違いはとても重要なため、理解しておきましょう。（参考：<a href="https://ja.wikipedia.org/wiki/%E6%A8%99%E6%BA%96%E5%81%8F%E5%B7%AE#%E6%A8%99%E6%9C%AC%E3%81%AE%E6%A8%99%E6%BA%96%E5%81%8F%E5%B7%AE">Wikipedia 標本の標準偏差</a>）</p>
<p>使用するライブラリによって標準偏差のみを実装できるものと、不偏標準偏差も実装できるものに分かれます。表Fに本稿で使用するライブラリ及びメソッドにおける適応を記載しました。</p>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/05/図.png" alt="" /></p>
<p>NumpyとPandasでは引数のddofの値を指定することで標準偏差と不偏標準偏差を指定できます。表F内の「標準」は引数を指定しなかった場合を指します。ライブラリによって「標準」の引数が異なるため注意してください。scikit-learnのStandardScalerでは不偏標準偏差を指定することはできません。そのため、標本を用いたデータセットを使用する際はNumpyもしくはPandasを用いて実装する必要があります。</p>
<h4>標準化のPython 実装</h4>
<p>ここから標準化の実装に入ります。乱数に関しては正規化で作成したものを使用します。標準化の実装は下記の流れで行います。「1.scikit-learnによる標準化の実装」だけ標準化前後の点をプロットします。「2.Numpyによる標準化の実装」、「3.Pandasによる標準化の実装」では標準偏差と不偏標準偏差の両方の場合で結果を出力します。</p>
<ol>
<li>sckit-learnによる標準化の実装（正規化前後のプロットを含む）</li>
<li>Numpyによる標準化の実装</li>
<li>Pandasによる標準化の実装</li>
</ol>
<h5>1.sckit-learnによる標準化の実装（標準化前後のプロットを含む）</h5>
<p>乱数は生成してあるため、標準化の定義から行います。</p><pre class="crayon-plain-tag">#[IN]:

from sklearn.preprocessing import StandardScaler

# 標準化のクラスを定義
ms = StandardScaler()

#fit_transformを用いて、特徴量の平均値と標準化を計算してから特徴量のスケール変換を行う
data_std = ms.fit_transform(data_n)

#プロットを定義
image_show(data_n,data_std, 'Standardization')</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/05/スクリーンショット-2021-05-06-15.42.01.png" alt="" /></p><pre class="crayon-plain-tag">#[IN]:

#最初5行を表示
print(data_std[0:5])</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

[[ 1.8438581   0.39370492]
 [ 1.0552128   2.27487202]
 [ 1.94780276 -1.01398504]
 [ 1.02644172 -0.16992331]
 [-0.03133318  0.40437555]]</pre><p>
標準化後の点が赤でプロットされました。この出力を見ても、標準化が0付近に分布しているのが視覚的に分かると思います。グラフからも分かるように標準化は特定の範囲に収める変換ではありません。正規化の時と同様に、その他のメソッドについての一部も記載します。</p>
<p>fit()　　　　　　　　　 与えられた特徴量の平均値と標準偏差を計算する。<br />
fit_transform()　　　　 平均値と標準偏差を計算し、変換する。<br />
inverse_transform()　　標準化を元に戻す。<br />
transform()　　　　　　fit()で得られた値を用いて変換する。<br />
適応されたクラスは属性を持ちます。属性が持つ値についても一部表示します。</p><pre class="crayon-plain-tag">#[IN]:

print("各特徴量ごとの平均値は"+str(ms.mean_))
print("各特徴量ごとの標準偏差は"+str(ms.var_))
print("サンプル数は"+str(ms.n_samples_seen_))</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

各特徴量ごとの平均値は[3.89815116 4.02109161]
各特徴量ごとの標準偏差は[1.98314123 1.9149536 ]
サンプル数は200</pre><p>
「1.scikit-learnによる標準化の実装」については以上になります。正規化と同様にscikit-learnは便利ですが、内部の仕様を理解せずに使用することはお勧めしません。scikit-learnの公式ドキュメントもあるため、詳細な変数などはこちらでも確認しておきましょう。（参考：<a href="https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html#sklearn.preprocessing.StandardScaler">sklearn.preprocessing.StandardScaler</a>）</p>
<h5>2.Numpyによる標準化の実装</h5>
<p>次にNumpyのメソッド用いて実装を行なっていきます。Numpyのメソッドを用いた実装の場合はscikit-learnとは違い標準化の計算式を定義してあげる必要があります。今回はstandard_n()という関数を作成し、正規化後の値を返すようにします。</p><pre class="crayon-plain-tag">#[IN]:

def standard_n(n, axis = None, ddof = 0):
  #平均値を計算
  mean_n = n.mean(axis = axis, keepdims = True)
  #標準偏差を計算 ddof=0なら標準偏差、ddof=1なら不偏標準偏差
  std_n = n.std(axis = axis, keepdims = True, ddof = ddof)
  #標準化の計算
  standard_n = (n - mean_n) / std_n
  return standard_n</pre><p>
</p><pre class="crayon-plain-tag">#[IN]:

print("標準化【標準偏差】↓")
print(standard_n(data_n,0,0)[0:5])
print("標準化【不偏標準偏差】↓")
print(standard_n(data_n,0,1)[0:5])</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

標準化【標準偏差】↓
[[ 1.8438581   0.39370492]
 [ 1.0552128   2.27487202]
 [ 1.94780276 -1.01398504]
 [ 1.02644172 -0.16992331]
 [-0.03133318  0.40437555]]
標準化【不偏標準偏差】↓
[[ 1.83924267  0.39271942]
 [ 1.05257146  2.26917771]
 [ 1.94292715 -1.0114469 ]
 [ 1.0238724  -0.16949797]
 [-0.03125475  0.40336335]]</pre><p>
標準偏差を用いた標準化の出力結果が「1.scikit-leanによる標準化の実装」と一致しました。不偏標準偏差を使用した標準化の出力については次の「3.Pandasによる標準化の実装」と比較します。計算式で難しい部分はありませんが、平均と標準偏差を求める際にkeepdims引数にTrueを渡しています。これは配列の次元数を落とさずに結果を求めるための引数です。慣れていない方には少し難しく感じるかもしれません。下記の記事を参考にしてみてください。（参考：<a href="https://snowtree-injune.com/2020/05/03/keepdims-z006/">NumPy♪関数maxやsumにおけるkeepdims指定の図解</a>）</p>
<h5>3.Pandasによる標準化の実装</h5>
<p>最後にPandasのメソッドを用いて標準化を実装します。こちらも正規化と同様にDataFrame型であったとしてもscikit-learnを用いて正規化は実装できます。その際、返り値はNumpyになることも同様です。standard_p()という関数を作成し、正規化後の値を返すようにします。この関数の返り値はDataFrame型のままになります。</p><pre class="crayon-plain-tag">#[IN]:

#標準化を行う関数の定義
def standard_p(p, ddof = 1):
  #最小値の計算
  mean_p = p.mean()
  #最大値の計算
  std_p = p.std(ddof = ddof)
  #標準化の計算
  standard_p = (p - mean_p) / (std_p)
  return standard_p</pre><p>
</p><pre class="crayon-plain-tag">#[IN]:

print("標準化【標準偏差】↓")
print(standard_p(data_p,ddof=0).head(5))
print("標準化【不偏標準偏差】↓")
print(standard_p(data_p,ddof=1).head(5))</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

標準化【標準偏差】↓
          0         1
0  1.843858  0.393705
1  1.055213  2.274872
2  1.947803 -1.013985
3  1.026442 -0.169923
4 -0.031333  0.404376
標準化【不偏標準偏差】↓
          0         1
0  1.839243  0.392719
1  1.052571  2.269178
2  1.942927 -1.011447
3  1.023872 -0.169498
4 -0.031255  0.403363</pre><p>
標準偏差を用いた標準化の結果が「1.scikit-learnによる標準化の実装」、「2.Numpyによる標準化の実装」と同じ値が出力されています。不偏標準偏差を用いた標準化も同様に、「2.Numpyによる標準化の実装」と同じ値が出力されています。若干数値が違う箇所に関しては表示されている桁数の影響であるため問題ありません。標準化の実装については以上になります。後半の実装でも標準化は登場するので、しっかりと理解しておいてください。</p>
<h4>正規化と標準化のどちらを使用するべきか</h4>
<p>正規化と標準化についてある程度理解できたかと思いますが、どちらを使用するべきか疑問に思う方もいるのではないでしょうか。この問題に関しては、使用するモデルや精度と比較しながら検討することであるため、一概に決めることはできません。例えば正規化は外れ値に敏感であると述べましたが、外れ値が存在するというだけで使用しないわけではありません。外れ値を除外すれば当然正規化を用いることができる場合もあります。</p>
<p>こういった判断は知識と経験の積み重ねで実現されます。与えられた課題に対して今までの経験や知識から、どちらを使用するべきか判断するという場合が多いです。難しいと感じるかもしれませんが、色々なデータセットで試してみながら知見を深めることが重要です。本稿も活用しながら少しづつ学習を進めていきましょう。</p>
<h3>正規化・標準化を用いたワインの分類</h3>
<p>本節ではscikit-learnを用いて利用できるワイン認識のデータセットを用いて機械学習における分類問題を実装します。本稿での実装では主に正規化と標準化を適応する場合と適応しない場合の比較を中心に行います。EDAなどは詳細には行いません。EDAなどについて知りたい方はAIマガジンの<a href="https://www.codexa.net/basic-exploratory-data-analysis-with-python/">【データサイエンティスト入門編】探索的データ解析（EDA）の基礎操作をPythonを使ってやってみよう</a>を参考にしてみてください。</p>
<p>正規化と標準化の解説時にライブラリがインポートされているかもしれませんが、この節ではもう一度使用するライブラリをインポートし直します。ここから先のコードだけでもワインの分類は行うことができます。</p><pre class="crayon-plain-tag">#[IN]:

import pandas as pd
import numpy as np
import warnings
warnings.filterwarnings('ignore')</pre><p>
ワインデータの読み込みを行います。ワインデータの基礎知識は下記の通りです。</p>
<ul>
<li>ワインのデータ数→178</li>
<li>ワインの種類→3クラス(class_0(59)、class_1(71)、class_2(48) )</li>
<li>各特徴量
<ol>
<li>Alcohol：アルコール</li>
<li>Malic acid：リンゴ酸</li>
<li>Ash：灰分</li>
<li>Alcalinity of ash：灰分のアルカリ性</li>
<li>Magnesium：マグネシウム</li>
<li>Total phenols：全フェノール</li>
<li>Flavanoids：フラボノイド</li>
<li>Nonflavanoid phenols：非フラバノイドフェノール類</li>
<li>Proanthocyanins：プロアントシアニン</li>
<li>Color intensity：色彩強度</li>
<li>Hue：色調</li>
<li>OD280/OD315 of diluted wines：蒸留ワインのOD280/OD315</li>
<li>Proline：プロリン</li>
</ol>
</li>
</ul>
<pre class="crayon-plain-tag">#[IN]:

from sklearn.datasets import load_wine
#ワインのデータセットを読み込み
data_wine = load_wine()
data_wine</pre>
<pre class="crayon-plain-tag">#[OUT]:

{'DESCR': '.. _wine_dataset:\n\nWine recognition dataset\n------------------------\n\n**Data Set Characteristics:**\n\n    :Number of Instances: 178 (50 in each of three classes)\n    :Number of Attributes: 13 numeric, predictive attributes and the class\n    :Attribute Information:\n \t\t- Alcohol\n \t\t- Malic acid\n \t\t- Ash\n\t\t- Alcalinity of ash  \n \t\t- Magnesium\n\t\t- Total phenols\n \t\t- Flavanoids\n \t\t- Nonflavanoid phenols\n \t\t- Proanthocyanins\n\t\t- Color intensity\n \t\t- Hue\n \t\t- OD280/OD315 of diluted wines\n \t\t- Proline\n\n    - class:\n            - class_0\n            - class_1\n            - class_2\n\t\t\n    :Summary Statistics:\n    \n    ============================= ==== ===== ======= =====\n                                   Min   Max   Mean     SD\n    ============================= ==== ===== ======= =====\n    Alcohol:                      11.0  14.8    13.0   0.8\n    Malic Acid:                   0.74  5.80    2.34  1.12\n    Ash:                          1.36  3.23    2.36  0.27\n    Alcalinity of Ash:            10.6  30.0    19.5   3.3\n    Magnesium:                    70.0 162.0    99.7  14.3\n    Total Phenols:                0.98  3.88    2.29  0.63\n    Flavanoids:                   0.34  5.08    2.03  1.00\n    Nonflavanoid Phenols:         0.13  0.66    0.36  0.12\n    Proanthocyanins:              0.41  3.58    1.59  0.57\n    Colour Intensity:              1.3  13.0     5.1   2.3\n    Hue:                          0.48  1.71    0.96  0.23\n    OD280/OD315 of diluted wines: 1.27  4.00    2.61  0.71\n    Proline:                       278  1680     746   315\n    ============================= ==== ===== ======= =====\n\n    :Missing Attribute Values: None\n    :Class Distribution: class_0 (59), class_1 (71), class_2 (48)\n    :Creator: R.A. Fisher\n    :Donor: Michael Marshall (MARSHALL%PLU@io.arc.nasa.gov)\n    :Date: July, 1988\n\nThis is a copy of UCI ML Wine recognition datasets.\nhttps://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data\n\nThe data is the results of a chemical analysis of wines grown in the same\nregion in Italy by three different cultivators. There are thirteen different\nmeasurements taken for different constituents found in the three types of\nwine.\n\nOriginal Owners: \n\nForina, M. et al, PARVUS - \nAn Extendible Package for Data Exploration, Classification and Correlation. \nInstitute of Pharmaceutical and Food Analysis and Technologies,\nVia Brigata Salerno, 16147 Genoa, Italy.\n\nCitation:\n\nLichman, M. (2013). UCI Machine Learning Repository\n[https://archive.ics.uci.edu/ml]. Irvine, CA: University of California,\nSchool of Information and Computer Science. \n\n.. topic:: References\n\n  (1) S. Aeberhard, D. Coomans and O. de Vel, \n  Comparison of Classifiers in High Dimensional Settings, \n  Tech. Rep. no. 92-02, (1992), Dept. of Computer Science and Dept. of  \n  Mathematics and Statistics, James Cook University of North Queensland. \n  (Also submitted to Technometrics). \n\n  The data was used with many others for comparing various \n  classifiers. The classes are separable, though only RDA \n  has achieved 100% correct classification. \n  (RDA : 100%, QDA 99.4%, LDA 98.9%, 1NN 96.1% (z-transformed data)) \n  (All results using the leave-one-out technique) \n\n  (2) S. Aeberhard, D. Coomans and O. de Vel, \n  "THE CLASSIFICATION PERFORMANCE OF RDA" \n  Tech. Rep. no. 92-01, (1992), Dept. of Computer Science and Dept. of \n  Mathematics and Statistics, James Cook University of North Queensland. \n  (Also submitted to Journal of Chemometrics).\n',
 'data': array([[1.423e+01, 1.710e+00, 2.430e+00, ..., 1.040e+00, 3.920e+00,
         1.065e+03],
        [1.320e+01, 1.780e+00, 2.140e+00, ..., 1.050e+00, 3.400e+00,
         1.050e+03],
        [1.316e+01, 2.360e+00, 2.670e+00, ..., 1.030e+00, 3.170e+00,
         1.185e+03],
        ...,
        [1.327e+01, 4.280e+00, 2.260e+00, ..., 5.900e-01, 1.560e+00,
         8.350e+02],
        [1.317e+01, 2.590e+00, 2.370e+00, ..., 6.000e-01, 1.620e+00,
         8.400e+02],
        [1.413e+01, 4.100e+00, 2.740e+00, ..., 6.100e-01, 1.600e+00,
         5.600e+02]]),
 'feature_names': ['alcohol',
  'malic_acid',
  'ash',
  'alcalinity_of_ash',
  'magnesium',
  'total_phenols',
  'flavanoids',
  'nonflavanoid_phenols',
  'proanthocyanins',
  'color_intensity',
  'hue',
  'od280/od315_of_diluted_wines',
  'proline'],
 '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, 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, 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]),
 'target_names': array(['class_0', 'class_1', 'class_2'], dtype='&lt;U7')}</pre>
ワインのデータセットを読み込むことができました。Pandasを用いて予測に必要なラベルを抽出します。<br />
<pre class="crayon-plain-tag">#[IN]:

#ワインラベルの抽出
df_label = pd.DataFrame(data_wine["target"], columns=["wine"])
#先頭5行を表示
df_label.head()</pre>
<img src="https://www.codexa.net/wp-content/uploads/2021/05/スクリーンショット-2021-05-06-16.20.28.png" alt="" /></p>
<p>次に各特徴量もラベルと同様にDataFrame型にして抽出します。</p><pre class="crayon-plain-tag">#[IN]:

#各特徴量を抽出
df_data = pd.DataFrame(data_wine["data"], columns = data_wine["feature_names"])
#先頭5行を表示
df_data.head()</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/05/スクリーンショット-2021-05-06-16.21.51.png" alt="" /></p>
<p>各特徴量の値が表示されました。このワインのデータセットは訓練データと検証用データに分かれていません。そのため、2分割する必要があります。このデータセットは初学者向けで、比較的簡単な部類に入ります。そのため、本稿では半分を訓練用、もう半分を検証用データとして扱いたいと思います。分割する際ですが、ワインのラベルの割合を等しくするため、stratify引数でラベルを指定します。この引数は他のデータセットを用いる際にも便利なので覚えておくことをお勧めします。</p><pre class="crayon-plain-tag">#[IN]:

from sklearn.model_selection import train_test_split
train_x, test_x, train_y, test_y = train_test_split(df_data, df_label, test_size=0.5,stratify=df_label["wine"],random_state=0)</pre><p>
訓練用と検証用に特徴量と各ラベルを分割できました。ここから正規化と標準化の実装に移ります。まずは正規化と標準化のクラスを定義します。scikit-learnを用いて実装します。</p><pre class="crayon-plain-tag">#[IN]:

from sklearn.preprocessing import StandardScaler,MinMaxScaler
#正規化のクラスを生成
mmsc=MinMaxScaler()
#標準化のクラスを生成
stdsc = StandardScaler()</pre><p>
データセットに正規化や標準化を適応する際に1つ注意点があります。訓練データと検証データに適用するクラスは同じものを使用してください。別々にfit()やfit_transform()メソッドを用いると、正規化や標準化の計算条件が異なってしまう可能性があります。そのため、訓練用データでfit()やfit_transform()メソッドを用いて正規化や標準化のパラメータを計算し、検証用データにはtransform()メソッドを用いてスケーリングを行いましょう。スケーリング後の値に関して確認したい方はコメントアウト「#」を外していただければ表示されます。</p><pre class="crayon-plain-tag">#[IN]:

#注意
#→訓練データでfitした変換器を用いて検証データを変換すること

#訓練用のデータを正規化
train_mm = mmsc.fit_transform(train_x)
#訓練用のデータを標準化
train_std = stdsc.fit_transform(train_x)

#訓練用データを基にテストデータを正規化
test_mm=mmsc.transform(test_x)
#訓練用データを基にテストデータを標準化
test_std = stdsc.transform(test_x)

#コメントアウトを外すとスケーリング後の値を確認できる
#print(train_mm)
#print(train_std)
#print(test_mm)
#print(test_std)</pre><p>
これで準備は整いました。今回は機械学習モデルの定番である「K近傍法」「パーセプトロン」「ランダムフォレスト」を用いて、「元のデータ」「正規化したデータ」「標準化したデータ」の正解率を比較します。</p>
<ol>
<li>K近傍法</li>
<li>パーセプトロン</li>
<li>ランダムフォレスト</li>
</ol>
<h4>1.K近傍法</h4>
<p>最初のモデルはK近傍法です。K近傍法に関する詳細な説明は省きますが、簡単に解説すると「新しいサンプルと特徴が似ているいくつかのサンプルのラベルを参考にして、新しいサンプルのラベルを予測する分類手法」です。具体的にどのようなアルゴリズムになっているか理解したい方は下記のサイトを参考に調べてみてください。（参考：<a href="https://ja.wikipedia.org/wiki/K%E8%BF%91%E5%82%8D%E6%B3%95">Wikipedia K近傍法</a>）</p>
<p>K近傍法を定義し学習させます。パラメータにはデフォルトの値を用います。デフォルトでは新しいサンプルから近い5個のラベルを用いて判定します。K近傍法に関する詳細な解説はscikit-learnの公式ドキュメントを参考にしてください。（参考：<a href="https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html">sklearn.neighbors.KNeighborsClassifier</a>）</p>
<p>各データ用にK近傍法を定義し、データを適用します。</p><pre class="crayon-plain-tag">#[IN]:

from sklearn.neighbors import KNeighborsClassifier

#元のデータ用
lr = KNeighborsClassifier()
#正規化したデータ用
lr_mm = KNeighborsClassifier()
#標準化したデータ用
lr_std = KNeighborsClassifier()

#元のデータの適用
lr.fit(train_x, train_y)
#正規化したデータの適用
lr_mm.fit(train_mm, train_y)
#標準化したデータの適用
lr_std.fit(train_std, train_y)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
                     metric_params=None, n_jobs=None, n_neighbors=5, p=2,
                     weights='uniform')</pre><p>
各データの予測結果を求め、正解率を出力します。score()メソッドは正解率（Accurcy）を返します。評価指標に関しては<a href="https://www.codexa.net/ml-evaluation-cls/">機械学習の評価指標 分類編：適合率や再現率、AUC（ROC曲線、PR曲線）を解説</a>に詳細に記載されています。</p><pre class="crayon-plain-tag">#[IN]:

print('元のデータのスコア :',lr.score(test_x, test_y))
print('正規化したデータのスコア :',lr_mm.score(test_mm, test_y))
print('標準化したデータのスコア :',lr_std.score(test_std, test_y))</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

元のデータのスコア : 0.7752808988764045
正規化したデータのスコア : 0.9662921348314607
標準化したデータのスコア : 0.9662921348314607</pre><p>
それぞれの正解率が表示されました。出力を見比べると元のデータのスコアだけが低く、正規化や標準化を行なったデータに対する正解率は0.9を超えています。このことから正規化や標準化がK近傍法に有効であることが分かります。</p>
<h4>2.パーセプトロン</h4>
<p>次にニューラルネットワークの基礎でもあるパーセプトロンを用いてワインの分類を行います。K近傍法と同様にモデルのパラメータはデフォルトに設定されているものを使用します。パーセプトロンは最近の機械学習分野において必須とも言える知識なので、確実に押さえておきましょう。パーセプトロンやニューラルネットワークについての知識はAIマガジンの<a href="https://www.codexa.net/what-is-deep-learning/">今時のエンジニアが知っておくべきディープラーニングの基礎知識</a>を参考にしてみてください。また、scikit-learnのパーセプトロンの引数については公式ドキュメントを参考にしてください。（参考：<a href="https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.Perceptron.html">sklearn.linear_model.Perceptron</a>）</p>
<p>各データ用にパーセプトロンを定義し、データを適用します。</p><pre class="crayon-plain-tag">#[IN]:

from sklearn.linear_model import Perceptron

#元のデータ用
lr = Perceptron(random_state = 0)
#正規化したデータ用
lr_mm = Perceptron(random_state = 0)
#標準化したデータ用
lr_std = Perceptron(random_state = 0)

#元のデータの適用
lr.fit(train_x, train_y)
#正規化したデータの適用
lr_mm.fit(train_mm, train_y)
#標準化したデータの適用
lr_std.fit(train_std, train_y)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

Perceptron(alpha=0.0001, class_weight=None, early_stopping=False, eta0=1.0,
           fit_intercept=True, max_iter=1000, n_iter_no_change=5, n_jobs=None,
           penalty=None, random_state=0, shuffle=True, tol=0.001,
           validation_fraction=0.1, verbose=0, warm_start=False)</pre><p>
K近傍法と同様に正解率を出力します。</p><pre class="crayon-plain-tag">#[IN]:

print('元のデータのスコア :',lr.score(test_x, test_y))
print('標準化したデータのスコア :',lr_std.score(test_std, test_y))
print('正規化したデータのスコア :',lr_mm.score(test_mm, test_y))</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

元のデータのスコア : 0.5280898876404494
標準化したデータのスコア : 0.9887640449438202
正規化したデータのスコア : 0.9775280898876404</pre><p>
それぞれの正解率が表示されました。元のデータに対する正解率を見ると、K近傍法よりもさらに低くなっています。もとの正規化や標準化を行なったデータに対する正解率は0.9を超えています。このことから正規化や標準化がパーセプトロンに有効であることが分かります。</p>
<h4>3.ランダムフォレスト</h4>
<p>最後にツリーベース型のモデルであるランダムフォレストを用いてワインの分類を行います。スケーリングの解説で、ツリーベース型のモデルは正規化や標準化にそれほど影響されにくいと記載しました。どの程度の違いに収まるのか、ここで検証していきます。</p>
<p>各データ用にランダムフォレストを定義し、データを適用します。</p><pre class="crayon-plain-tag">#[IN]:

from sklearn.ensemble import RandomForestClassifier
#元のデータ用
lr = RandomForestClassifier(random_state = 0)
#正規化したデータ用
lr_mm = RandomForestClassifier(random_state = 0)
#標準化したデータ用
lr_std = RandomForestClassifier(random_state = 0)

#元のデータの適用
lr.fit(train_x, train_y)
#正規化したデータの適用
lr_mm.fit(train_mm, train_y)
#標準化したデータの適用
lr_std.fit(train_std, train_y)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

RandomForestClassifier(bootstrap=True, ccp_alpha=0.0, class_weight=None,
                       criterion='gini', max_depth=None, max_features='auto',
                       max_leaf_nodes=None, max_samples=None,
                       min_impurity_decrease=0.0, min_impurity_split=None,
                       min_samples_leaf=1, min_samples_split=2,
                       min_weight_fraction_leaf=0.0, n_estimators=100,
                       n_jobs=None, oob_score=False, random_state=0, verbose=0,
                       warm_start=False)</pre><p>
K近傍法やパーセプトロンと同様に正解率を出力します。</p><pre class="crayon-plain-tag">#[IN]:

print('元データのスコア :',lr.score(test_x, test_y))
print('正規化データのスコア :',lr_mm.score(test_mm, test_y))
print('標準化データのスコア :',lr_std.score(test_std, test_y))</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

元データのスコア : 0.9887640449438202
正規化データのスコア : 0.9887640449438202
標準化データのスコア : 0.9887640449438202</pre><p>
それぞれの正解率が表示されました。全てのデータに対しての正解率は0.9台と非常に高く、値もほぼ等しくなっています。この結果からツリーベース型のモデルがスケーリングの影響を受けにくいことがご理解いただけたと思います。</p>
<table>
<tbody>
<tr>
<td rowspan="3">K近傍法</td>
<td>元データ</td>
<td>0.7752808988764045</td>
</tr>
<tr>
<td>正規化</td>
<td>0.9662921348314607</td>
</tr>
<tr>
<td>標準化</td>
<td>0.9662921348314607</td>
</tr>
<tr>
<td rowspan="3">パーセプトロン</td>
<td>元データ</td>
<td>0.5280898876404494</td>
</tr>
<tr>
<td>正規化</td>
<td>0.9887640449438202</td>
</tr>
<tr>
<td>標準化</td>
<td>0.9775280898876404</td>
</tr>
<tr>
<td rowspan="3">ランダムフォレスト</td>
<td>元データ</td>
<td>0.9887640449438202</td>
</tr>
<tr>
<td>正規化</td>
<td>0.9887640449438202</td>
</tr>
<tr>
<td>標準化</td>
<td>0.9887640449438202</td>
</tr>
</tbody>
</table>
<p>モデルによってスケーリングの影響は様々です。そのため、様々なモデルに対して、スケーリングを行い評価を比較してみることをお勧めします。scikit-leanに用意されているデータセットは比較的簡単に分類できるため初学者の方にも扱いやすいと思います。もう少し難しいデータセットを扱いたい方はkaggleなどのコンペティションに参加してみてください。（参考：<a href="https://scikit-learn.org/stable/modules/classes.html#module-sklearn.datasets">sklearn.datasets: Datasets</a>・<a href="https://www.kaggle.com/">kaggle</a>）</p>
<h3>まとめ</h3>
<p>正規化・標準化に関する解説と実装は以上です。モデルやデータセットによって手法が変わるので、難しく感じるかもしれませんが機械学習の面白さはそこにあると筆者は思います。本稿が少しでも皆様の学習の参考になれば幸いです。</p>
<p>codexaでは初学者や独学者向けに機械学習のコースを提供しています。ライブラリや統計的知識、モデルのアルゴリズムに興味がある方は是非受講してみてください。お待ちしております。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.codexa.net/normalization-python/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>外れ値とは？Pythonを使用して外れ値の検出方法を実装してみよう（全コード公開中）</title>
		<link>https://www.codexa.net/python-outlier/</link>
					<comments>https://www.codexa.net/python-outlier/#respond</comments>
		
		<dc:creator><![CDATA[codexaチーム]]></dc:creator>
		<pubDate>Thu, 27 May 2021 05:30:02 +0000</pubDate>
				<category><![CDATA[機械学習]]></category>
		<guid isPermaLink="false">https://www.codexa.net/?p=2946</guid>

					<description><![CDATA[データを扱っていると想定外の値を目にすることがあります。筆者も毎日の体重を体重計からスマートフォンに転送し記録していますが、体重計の故障などによって普段であれば考えられない体重が記録されていることがあります。正直、サービ [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>データを扱っていると想定外の値を目にすることがあります。筆者も毎日の体重を体重計からスマートフォンに転送し記録していますが、体重計の故障などによって普段であれば考えられない体重が記録されていることがあります。正直、サービスの利用者からすれば、意味のない値は削除してほしいものです。</p>
<p>機械学習の分野でも通常時のデータから外れた値というのはよく目にします。膨大なデータが求められる機械学習ですが、量が多くなればなるほど、誤差やミスなどが発生する確率は高くなります。シンプルに考えるのであれば、除去を行えば問題ないのかもしれません。しかし、データ分析を行うに当たって安易に値を削除することは重要な事態を引き起こす可能性があります。</p>
<p>本稿では機械学習でよく耳にする「外れ値」について解説していきます。その後、Pythonを使用して外れ値の検出方法を実装していきます。機械学習を勉強し始めた方や、データサイエンティストを目指されている人にとって非常に重要な内容のため、しっかりと押さえましょう。</p>
<h3>外れ値と異常値</h3>
<p>本節では、外れ値と異常値について詳しく解説していきます。これらの言葉の概念は最も初歩的ですが、理解していないと先に進むことができません。図を用いながら可能な限り直感的に、正しく理解できるように解説して行きます。</p>
<h4>外れ値とは</h4>
<p>外れ値（outlier）とは測定された値の中で他のデータとかけ離れているものを指します。実験結果を記録している中で、他のデータの分布とは明らかに異なる場所に数値が出現したりする際に外れ値と呼ばれることが多いです。外れ値が発生する原因は様々ですが、そのままにしておくと、データ分析の際に統計指標を歪める可能性があるため、何らかの対処が必要な場合があります。図1と図2のグラフは外れ値が存在する典型的な例です。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/04/分布内の外れ値.png" alt="" /><br />
図1と図2を見てみましょう。図1がヒストグラムで図2が散布図です。赤い丸で囲われた部分の値が他のデータとは異なる位置に存在していることが見て取れると思います。どちらも軸の単位が記載されていないため厳密に外れ値と断定できないという話はありますが、ここでは直感的に理解できるように外れ値として扱います。</p>
<h4>異常値とは</h4>
<p>多くの文献で、外れ値と並んで紹介されるのが、異常値です。外れ値の一種と表現されることもありますが、言葉が持つ意味としては異なります。異常値は外れ値の中で入力ミスや測定ミス等が原因となっているものを指します。つまり、物理的にとりうるはずがない値ということです。例えばものの数を測定するときには、通常は0もしくは正の値しかとりません。その中で負の値を持ったデータがあるなど、とりうるはずのない値が異常値として判定されます。表Aのような倉庫内の製品と在庫数の関係を例にして異常値を確認して見ましょう。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/04/異常値.png" alt="" /><br />
表Aを見て見ましょう。製品「D」が-12個という値を取っています。在庫数のカウントにおいて値が負の数をとることはまずありません。そのため、製品「D」の-12という値は外れ値であり、異常値として扱うことができます。当然、この値を無視することはできません。異常値である以上何らかの処理を行い対処する必要があります。</p>
<h3>外れ値が及ぼす影響</h3>
<p>前節で外れ値と異常値について解説しました。本節では、外れ値が実際に及ぼす影響について解説していきます。当然ですがデータセットによって外れ値がもたらす影響は異なります。ここでは人為的に作成したデータで欠損値の影響を再現しますが、実際には使用しているデータセット毎に考えなければならないことを理解しておいてください。影響の例としては以下などが挙げられます。</p>
<ul>
<li>外れ値による統計指標の歪み</li>
<li>作業コストの増加</li>
<li>データ分析の精度の低下　など</li>
</ul>
<p>このような影響を外れ値は及ぼします。もう少し詳細に理解するために、次は実際の数値を使用して見ていきます。</p>
<h4>外れ値が及ぼす影響（1次元）</h4>
<p>外れ値は統計指標を歪ませる場合があると説明しました。通常のデータとはかけ離れた位置に存在している数値の存在により、数値の平均や標準偏差に影響を与える場合があります。まずは理解しやすい1次元のデータを示します。表Bと表Cはあるクラスの生徒の身長をまとめたものです。2つの表を見比べて何が起こっているかを確認してみます。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/04/外れ値が及ぼす影響1次元.png" alt="" /><br />
表Bと表Cを見比べると、表Cには外れ値が存在し、生徒「G」の値が明らかに不自然なことがわかります。表Bを確認してみると本来の値は「152cm」であったことがわかります。つまり、何かしらの要因により身長がメートルで記載されてしまったと考えられます。ちなみに「1.52cm」の身長の生徒が存在する確率は限りなく0です。そのため、人的ミスであることが確認できればほぼ「異常値」として断定していいと言えます。</p>
<p>次は平均に注目します。外れ値がない場合は「165.80」ですが、外れ値がある場合は「150.75」になっています。これが統計指標の歪みです。今回は生徒の人数が10人だけであるため、影響がかなり大きくで出ています。正しいデータの数がもっと多ければ、外れ値から受ける影響も少なくなりますが、それでも外れ値が表Cにとって悪影響であることは間違いありません。</p>
<h4>外れ値が及ぼす影響（2次元）</h4>
<p>2次元についても解説していきます。機械学習の分野は多次元データから予測することがほとんどですが、2次元の方が視覚的に理解しやすいため解説ではそちらを使用します。異なる点は次元数ですが、そこまで大きな違いがあるわけではありません。そのため、今回の解説が多次元でも同様に発生すると考えていただくと、理解しやすいと思います。図3、図4ではある企業での20歳〜40歳までの年齢（歳）と平均年収（万円）の相関が高いと仮定したプロットに、近似直線を描画しています。2つの図を見比べて何が起こっているかを確認していきます。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/04/外れ値が及ぼす影響2次元.png" alt="" /><br />
図3と図4を見比べると、外れ値がない場合は年齢が上がるにつれて平均年収も高いことがわかります。しかし、外れ値がある場合はどうでしょうか。21歳の平均年収が1500万になっているだけで、その他の値は全て同じに設定されています。しかし、近似的には年齢が上がるにつれて平均年収が下がると読み取ることができます。このように2次元でも外れ値は全体のデータに影響を与える場合があります。多次元の場合も同様で外れ値に対して全体のデータが引っ張られる場合があります。</p>
<p>大きな影響を与えている21歳の平均年収ですが、この外れ値の原因を少し考えています。例としては以下のようなことが挙げられます。</p>
<ul>
<li>入力を間違えたことによる人的ミス</li>
<li>本当に対象企業の21歳の平均年収が1500万である　など</li>
</ul>
<p>このように、外れ値に対しては色々な考えを挙げることができます。外れ値の確かな原因が不明なときは仮説を立ててデータ分析を進めていく必要もあります。ただデータを全体的に眺めて流すのではなく、データが持つ意味を考えながら外れ値に対応していくことが重要です。</p>
<h3>外れ値の検出方法</h3>
<p>ここまで、外れ値の基本的な概要について解説してきました。ここからは実際に外れ値の検出方法について解説していきます。一概に外れ値の検出方法と言っても様々な手法があります。重要なことはデータセットに合わせた正しい手法の選択と、正しい外れ値を検出できることです。本説では1次元と多次元の場合に分けて解説します。</p>
<h4>1次元のデータの場合</h4>
<p>最初に1次元データの時の外れ値の検出方法です。外れ値の検出法の中では最も基礎となる部分です。多次元データだとしても、1次元ずつ外れ値を見ていくこともできます。汎用性が高いというメリットがあるため、初学者の方もしっかりと理解しておきましょう。</p>
<h5>四分位範囲（IQR）と箱ひげ図</h5>
<p>最初に四分位範囲と箱ひげ図について解説していきます。2つの概念には繋がりがあるため、その部分に注目しながら見ていきます。</p>
<p>1.四分位範囲</p>
<p>「四分位範囲（Interquartile range）」とはデータの広がり具合を示す指標の１つで、英名を略してIQRとも呼ばれます。四分位範囲は四分位数を使って求められます。四分位数は、データの値を小さい順に並べた時にデータを4つに分割する時の区切り値のことを指します。小さい順に第1四分位数（Q1）、第2四分位数（Q2）、第3四分位数（Q3）となり、第2四分位数はデータ全体の中央値を指します。第1四分位数と第3四分位数は、第2四分位数と最大値・最小値の範囲内の中央値を指します。四分位範囲の求め方は以下の通りです。前節で用いた表Cを小さい順に整頓したものを利用して表Dとして表示します。</p>
<p>四分位範囲（IQR) ＝ 第3四分位数（Q3) ー 第1四分位数（Q1）<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/04/四分位範囲の求め方.png" alt="" /><br />
四分位範囲を使って外れ値を検出できます。一般的に、四分位範囲を1.5倍に拡大し、そこから外れる値を外れ値とします。四分位範囲における外れ値は以下の式で表されます。四分位範囲と同じように前節で用いた表を利用して外れ値を計算し、表示します。</p>
<p>外れ値 ＜ 第1四分位数 ー （1.5 × 四分位範囲）</p>
<p>外れ値 ＞ 第3四分位数 ＋ （1.5 × 四分位範囲）</p>
<p>外れ値 ＜ 151.5　　　　　（151.5 ＝ 162 ー 1.5 × 7）</p>
<p>外れ値 ＞ 179.5　　　　　（179.5 ＝ 169 ＋ 1.5 × 7）</p>
<p>外れ値の求め方について理解できたでしょうか。上の計算結果であれば外れ値は「G」の1.52になります。四分位範囲を使用して外れ値を検出することができました。次はこの四分位範囲から求めた外れ値を可視化できる箱ひげ図について解説していきます。</p>
<p>2.箱ひげ図</p>
<p>箱ひげ図（box-and-whisker plot）は外れ値を可視化する際に非常に便利な図です。四分位範数を含めたデータの要約統計量を確認できます。<br />
<img loading="lazy" class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/04/箱ひげ図.png" alt="" width="483" height="747" /><br />
箱ひげ図はデータの四分位数と最大値、最小値を表示してくれます。最大値、最小値、外れ値の表示に関しては、箱ひげ図の設定によって異なり、上記に述べたような四分位範囲の1.5倍の範囲で表示することもあれば、外れ値を表示せずに、単にデータ全体の最大値と最小値を表示することもできます。図5では、この後の実装で外れ値を表示することになるので、外れ値を記載しました。</p>
<p>四分位範囲を用いた外れ値検出は、過程となる確率分布がありません。そのため、比較的、汎用性が高いという利点があります。また、他のデータの箱ひげ図と並べた時にデータのばらつきを簡単に可視化できます。しかし、データの数が少なかったりすると外れ値の検出がうまくいかない場合もあるため、注意が必要です。</p>
<h5>zスコア</h5>
<p>zスコア（z-score）はそれぞれのデータの点数を平均値を0、標準偏差を1になるように変換した値のことです。これによりデータ内の相対的な位置を知ることができます。zスコアによる外れ値検出は有意水準によって決定されます。有意水準が-2と2であれば、外れ値でないデータは95.45%の中に含まれることになり、-3と3では99.73%の中に含まれることになります。また、有意水準の基準については下記の通りです。より詳細に有意水準を決めたい方は正規分布について調べて見てください。箱ひげ図と同様に、前節でも利用した表Dを用いてzスコアによる外れ値を検出します。（参考：<a href="https://ja.wikipedia.org/wiki/%E6%AD%A3%E8%A6%8F%E5%88%86%E5%B8%83" target="_blank" rel="noopener">Wikipedia 正規分布</a>）</p>
<table>
<tbody>
<tr>
<td style="text-align: left;">有意水準</td>
<td style="text-align: left;">信頼度</td>
</tr>
<tr>
<td>&lt;-1.65 かつ +1.65&lt;</td>
<td>90%</td>
</tr>
<tr>
<td>&lt;-1.96 かつ +1.96&lt;</td>
<td>95%</td>
</tr>
<tr>
<td>&lt;-2 かつ +2&lt;</td>
<td>95.44%</td>
</tr>
<tr>
<td>&lt;-2.58 かつ +2.58&lt;</td>
<td>99%</td>
</tr>
<tr>
<td style="text-align: left;">&lt;-3 かつ +3&lt;</td>
<td style="text-align: left;">99.73%</td>
</tr>
</tbody>
</table>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/04/zスコア.png" alt="" /><br />
<img src="https://www.codexa.net/wp-content/uploads/2021/04/zスコア表示.png" alt="" /></p>
<p>生徒Gのzスコアは-2.98でした。有意水準が2であれば外れ値と判定できます。zスコアを用いた外れ値の検出には似たものとしてスミルノフ・グラブス検定も存在します。部分的には同じですが、スミルノフ・グラブス検定は有意水準の求め方が決まっており、再帰的に行うという特徴があります。（参考：<a href="https://ja.wikipedia.org/wiki/%E5%A4%96%E3%82%8C%E5%80%A4#%E3%82%B9%E3%83%9F%E3%83%AB%E3%83%8E%E3%83%95%E3%83%BB%E3%82%B0%E3%83%A9%E3%83%96%E3%82%B9%E6%A4%9C%E5%AE%9A" target="_blank" rel="noopener">Wikipedia スミルノフ・グラブス検定</a>）</p>
<p>注意点として、zスコアを使用した検定は、データ全体が正規分布に従っていることを仮定としている点を理解しておいてください。データの形状が正規分布に従わない場合、外れ値検出がうまく行かない場合が存在します。そのため、対数変換をすることでデータの分布の形状を変化させる必要があったりもします。この内容に触れると解説が長くなってしまうため、本稿では省きます。しかし、機械学習の分野では重要な内容のため、興味がある方は調べてみてください。</p>
<h5>トリム平均</h5>
<p>トリム平均とはデータ全体の最大値と最小値から一定の割合だけ削除し、残ったデータで計算した平均です。例えばデータの両側5%を削除した場合は「5%トリム平均」と言います。ここでも先ほどまでと同じ表Dを用いてトリム平均について解説していきます。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/04/トリム平均.png" alt="" /><br />
表Fは小さい順に並べたデータの両側から10%ずつを削除し、10%トリム平均を求めたものです。削除後の平均が本来の平均値（165.80）に近づきました。実際は、データ全体の20%を削除するようなことは、非常に稀です。今回は元からのデータ数が10個であることと分かりやすいことに重きを置いたことでこのような結果になっています。トリム平均は対象の値が有効かどうかの検証は行いません。実装は非常に簡単ですが、むやみに使用すると外れ値でない値まで削除する可能性があるため、注意が必要です。</p>
<h4>多次元データの場合</h4>
<p>次に多次元データに対する外れ値の検出方法です。1次元と比較して複雑なものも多いですが少しずつ理解していきましょう。</p>
<p>最初に多次元で外れ値を検出する必要性について解説します。機械学習では基本的に多次元データから予測を立てることが多いです。1次元の外れ値検出では検出できない分布の例は複数存在します。図6ではその1例を示した2次元のを示しています。<br />
<img src="https://www.codexa.net/wp-content/uploads/2021/04/2次元の外れ値の必要性.png" alt="" /><br />
図6は分布の真ん中に正しい値を取り、周囲に外れ値が存在してしまっている場合の分布です。このような分布の場合、x軸、もしくはy軸単体でデータを確認しても外れ値を検出できなくなってしまいます。そのため、2次元や多次元のまま外れ値を検出する必要があります。次に、多次元データに使用できる外れ値の検出方法の解説に移ります。ここでは<a href="https://www.codexa.net/scikit-learn-intro/">scikit-learn</a>で紹介されている外れ値検出の方法を取り上げます。多次元の外れ値検出の方法は他にも多く存在します。興味がある方は本稿で取り上げた以外のものについても調べてみてください。（参考：<a href="https://scikit-learn.org/stable/modules/outlier_detection.html#isolation-forest" target="_blank" rel="noopener">2.7. Novelty and Outlier Detection</a>）</p>
<h5>Isolation Forest</h5>
<p>Isolation Forestは<a href="https://www.codexa.net/decision-tree-random-forest/">決定木</a>を用いた外れ値検出です。与えられたデータ全体から特徴をランダムに選択し、その特徴の中の最大値と最小値の範囲から分割値を選択します。外れ値が存在していた場合、その部分の決定木の深さは他のノードまでの深さに比べて短くなります。Pythonでの実装の際には様々なパラメーターが用意されているので、確認してみてください。（参考：<a href="https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.IsolationForest.html" target="_blank" rel="noopener">sklearn.ensemble.IsolationForest</a>）</p>
<h5>LocalOutlierFactor（LOF）</h5>
<p>LOFは値に対する密度を用いた外れ値検出です。データ内のそれぞれの点から近くのk個までの値に対する局所密度を求め、比較します。外れ値が存在していた場合他の点が持つ局所密度よりも、密度が低くなります。これにより、外れ値を検出することができます。k個の値に関しては、実装の段階で指定することができます。（参考：<a href="https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.LocalOutlierFactor.html#sklearn.neighbors.LocalOutlierFactor" target="_blank" rel="noopener">sklearn.neighbors.LocalOutlierFactor</a>）</p>
<h5>OneClassSVM （OCSVM）</h5>
<p>OCSVMは<a href="https://www.codexa.net/support-vector-machine-tutorial/">SVM</a>の概念を利用した外れ値検出です。公式によると、本来OCSVMはパラメータの値によって外れ値検出に有用な結果をもたらす可能性があると述べられており、本来は新規性の検出に最適であると述べています。元々、OCSVMは外れ値に非常に影響を受けやすく、訓練用データに外れ値が潜んでいた場合、良い結果を得ることができません。正しい値を原点から遠くに、外れ値を原点付近に分布させるような特徴空間に写像させることにより外れ値を検出できます。写像の際に使用するカーネルなどは実装の際に選択できます。有効なカーネルを選択するためにもパラメータの確認は必須です。（参考：<a href="https://scikit-learn.org/stable/modules/generated/sklearn.svm.OneClassSVM.html#sklearn.svm.OneClassSVM" target="_blank" rel="noopener">sklearn.svm.OneClassSVM</a>）</p>
<h5>EllipticEnvelope</h5>
<p>EllipticEnvelopeはデータ全体が正規分布に従っていると仮定し、楕円を検出します。正規分布を仮定しているため、従わないデータセットに関しては検出がうまくいきません。それでも、1次元のデータの時にも述べたように、正規分布に近づける手段は存在するので組み合わせながら実装していくことをオススメします。（参考：<a href="https://scikit-learn.org/stable/modules/generated/sklearn.covariance.EllipticEnvelope.html#sklearn.covariance.EllipticEnvelope" target="_blank" rel="noopener">sklearn.covariance.EllipticEnvelope</a>）</p>
<p>これらの手法をおもちゃのデータセットを例に適用し、比較したものも公開されています。手法ごとのメリットとデメリットを把握するためにも、確認しておきましょう。（参考：<a href="https://scikit-learn.org/stable/auto_examples/miscellaneous/plot_anomaly_comparison.html#sphx-glr-auto-examples-miscellaneous-plot-anomaly-comparison-py" target="_blank" rel="noopener">Comparing anomaly detection algorithms for outlier detection on toy datasets</a>）</p>
<h3>外れ値に対する考え方</h3>
<p>ここまで、外れ値の概要から検出方法まで解説してきました。ここまで理解した上で本節では改めて外れ値に対する考え方・向き合い方について筆者の意見も含めながら解説していきます。</p>
<h4>異常値であるかを検討する</h4>
<p>異常値であるかはしっかりと検討する必要があります。明らかに単位が違っていたり、絶対に取り得るはずのない数値であれば異常値と判断する場合もあるかもしれませんが、実際のデータセットで分析者が独断で異常値と判断するのは慎重にならなければなりません。データ分析者とデータ収集者が別の場合、情報の差し違いなどにより、誤認してしまう可能性があります。「この異常値と思われる値はなぜ発生したのか」という理由まで確認した上で異常値と判断することが必要です。</p>
<h4>外れ値の除外は慎重に</h4>
<p>機械学習を始めたばかりの方の多くは<a href="https://www.codexa.net/tutorial-linear-regression-for-beginner/">線形回帰</a>や<a href="https://www.codexa.net/decision-tree-random-forest/">ランダムフォレスト</a>などのモデル構築から勉強していく場合が多いです。それは決して悪いことではありません。しかし、モデルの精度を上げることだけを目的とするとデータセット内の邪魔な値（外れ値含む）を極力無くし、汎用的なモデルを作成する方に考えをつぎ込みがちです。<a href="https://www.codexa.net/what-is-kaggle/">kaggle</a>などのデータ分析コンペティションにおいては単純にスコアを競う場合もあるため、推奨される場合もありますが、現実問題では慎重に考える必要もあります。</p>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/04/外れ値を削除すべきか.png" alt="" /><br />
時に外れ値というのは、それ自体が必要な情報である場合もあります。例えば工場などにおける異常検知などでは、エラー時の値というのが外れ値として記録されたりもします。外れ値（以上検知時の値）を排除して作成した機械学習モデルは通常の作業においては高い精度を出力できるモデルになりますが、異常検知という動作に関しては全くの無意味になる可能性があります。また、上記の図のようにデータ分析者が安易に外れ値を除外することで本来の目的を達成できない可能性もあります。そのため、外れ値への対応は目的と照らし合わせた上でしっかりと検討することが重要です。</p>
<h3>外れ値検出の実装</h3>
<p>本節では、外れ値検出をGoogle Colabを用いて実装していきます。本稿は2021年5月時点でコードの実行確認を行いましたので、Google Colabのデフォルトのバージョンが変更されない限り、ライブラリをそのままインポートすれば同じように実装可能です。是非、ご自身でも実装してみてください。（参考：<a href="https://www.codexa.net/how-to-use-google-colaboratory/">Google Colabの知っておくべき使い方 – Google Colaboratoryのメリット・デメリットや基本操作のまとめ</a>）</p>
<p>外れ値検出は主に1次元データに対応したものを中心に行なっていきます。多次元データの外れ値検出は直感的に理解しにくいため、本稿では散布図を用いて外れ値を可視化するところだけを行います。実際に多次元データから外れ値を検出されたい方はkaggleなどのコンペティションなどを活用しながら、色々なデータセットで試して見てください。（参考：<a href="https://www.kaggle.com/">kaggle</a>）</p>
<p>まずは必要なライブラリをインポートします。</p><pre class="crayon-plain-tag">#[IN]:

#ライブラリの読み込み
import numpy as np
import pandas as pd
import copy
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline</pre><p>
今回使用するのはアヤメのデータセットです。アヤメのデータセットに関する基本情報は以下の通りです。また、AIマガジンでも<a href="https://www.codexa.net/basic-exploratory-data-analysis-with-python/">【データサイエンティスト入門編】探索的データ解析（EDA）の基礎操作をPythonを使ってやってみよう</a>でアヤメのデータセットを扱っています。アヤメのデータセットの扱いが初めての方は、こちらも参考にしてみてください。</p>
<p>アヤメのデータセットのカラム名</p>
<ul>
<li>sepal_length（がく片の長さ cm）</li>
<li>sepal_width（がく片の幅 cm）</li>
<li>petal_length（花びらの長さ cm）</li>
<li>petal_width（花びらの幅 cm）</li>
<li>species（花の種類 setosa,versicolor,virginica）</li>
</ul>
<p>データセットを読み込みます。今回はseabornに用意されているデータセットを利用して実装を行います。ロードと花の種類の表示、データセットの先頭を表示します。（参考：<a href="https://seaborn.pydata.org/index.html">seaborn</a>）</p><pre class="crayon-plain-tag">#[IN]:

#アヤメのデータセットの読み込みと表示
df = sns.load_dataset("iris")
print(df.head())

#花の種類を表示
print("花の種類は"+str(df["species"].unique())+"です")</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/04/code1.png" alt="" /></p>
<h4>データを可視化</h4>
<p>早速、データ全体を可視化してみます。今回はsearbornのpairplotを用いて花の種類ごとに色を分けた散布図とヒストグラムを表示します。非常に簡単に実装できるため、汎用性の高い可視化方法です。他にも<a href="https://www.codexa.net/seaborn-python/">seaborn 徹底入門！Pythonを使って手軽で綺麗なデータ可視化８連発</a>で、seabornの可視化方法を取り上げています。参考にしてみてください。</p><pre class="crayon-plain-tag">#[IN]:

#花の種類(species)ごとに散布図を表示
sns.pairplot(df, hue='species', palette="husl")</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/04/スクリーンショット-2021-04-09-15.03.42.png" alt="" /><br />
特徴量同士の散布図が表示されました。この散布図だけを見るだけでも、少し外れ値と思われる点を見つけることができると思います。このように散布図は2変数間の関係を俯瞰的に可視化できます。ライブラリによる外れ値検出も重要ですが、視覚的に外れ値を確認することも極めて重要です。</p>
<h4>箱ひげ図</h4>
<p>まずはアヤメのデータセットの特徴量の中から、それぞれ外れ値を検出してみたいと思います。今後何度も箱ひげ図は表示することになるため、get_box()関数を定義して箱ひげ図を表示できるようにします。詳細に関しては省きますが、pandasのplot(&#8220;kind&#8221;=box)を用いて箱ひげ図を表示しています。</p><pre class="crayon-plain-tag">#[IN]:

#箱ひげ図を表示するための関数を定義
def get_box(input_df):
  #入力のコピーを作成
  output_df=input_df.copy()
  #表示する図のサイズを指定
  fig = plt.figure(figsize=(20,20))
  #箱ひげ図で表示するデータの列を指定
  num_list=["sepal_length","sepal_width","petal_length","petal_width"]
  #指定した列分繰り返す
  for i in range(len(num_list)):
    #1出力に複数の図を表示できるように設定
    plt.subplot(len(num_list), 4, i+1)
    #箱ひげ図の表示
    output_df[num_list[i]].plot(kind="box")
  return output_df</pre><p>
まずは花の種類を気にせずに外れ値を検出していきたいと思います。特に処理を行わず、最初に作成したデータフームをget_box()関数に引数として与えます。</p><pre class="crayon-plain-tag">#[IN]:

#sepal_length、sepal_width、petal_length、petal_widthのそれぞれの列から箱ひげ図を表示
df=get_box(df)</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/04/code2.png" alt="" /><br />
箱ひげ図を表示することができました。どうやらsepal_widthの外れ値を検出したようです。しかし、可視化することができただけで、外れ値の正確な情報が不明なため、詳細な情報を得るためにbox_Outlier()関数を定義します。この関数では箱ひげ図のそれぞれの四分位数の情報、外れ値の値とindex値を取得します。実際に上に表示されている箱ひげ図の情報を取得して見ます。</p><pre class="crayon-plain-tag">#[IN]:

#四分位数と外れ値のindex値を取得する関数を定義
def box_Outlier(input_df):
  #入力のコピーを作成
  output_df=input_df.copy()
  #詳細を表示するデータの列を指定
  num_list=["sepal_length","sepal_width","petal_length","petal_width"]
  #指定した列分繰り返す
  for i in range(len(num_list)):
    #第1四分位数を取得
    q1=output_df[num_list[i]].quantile(0.25)
    #第2四分位数を取得
    q3=output_df[num_list[i]].quantile(0.75)
    #IQRを取得
    iqr=q3-q1
    #外れ値基準の下限を取得
    bottom=q1-(1.5*iqr)
    #外れ値基準の上限を取得
    up=q3+(1.5*iqr)
    #列名、Q1、Q3、IQR、外れ値を表示
    print(str(num_list[i]))
    print("Q1は："+str(q1))
    print("Q3は："+str(q3))
    print("IQRは："+str(iqr))
    print("外れ値は↓")
    print(output_df[num_list[i]][(output_df[num_list[i]] &lt; bottom) | (output_df[num_list[i]] &gt; up) ])
    print("*********************")
  return output_df</pre><p>
</p><pre class="crayon-plain-tag">#[IN]:

#箱ひげ図の詳細を取得
df=box_Outlier(df)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

sepal_length
Q1は：5.1
Q3は：6.4
IQRは：1.3000000000000007
外れ値は↓
Series([], Name: sepal_length, dtype: float64)
*********************
sepal_width
Q1は：2.8
Q3は：3.3
IQRは：0.5
外れ値は↓
15    4.4
32    4.1
33    4.2
60    2.0
Name: sepal_width, dtype: float64
*********************
petal_length
Q1は：1.6
Q3は：5.1
IQRは：3.4999999999999996
外れ値は↓
Series([], Name: petal_length, dtype: float64)
*********************
petal_width
Q1は：0.3
Q3は：1.8
IQRは：1.5
外れ値は↓
Series([], Name: petal_width, dtype: float64)
*********************</pre><p>
箱ひげ図について詳細な情報を取得することができました。sepal_widthに存在した外れ値とindex値が取得できています。情報が取得できたので、これらの値を使用して、データフレームから削除することも可能ですし、他の値で埋めることもできます。当然、有効な対処をしなければならないことは忘れないようにしてください。</p>
<p>ここまで、検出してきたのはアヤメの種類については考えない場合の外れ値でした。しかし、この外れ値の検出方法は本当に正しいのでしょうか。もう一度、最初から考えてみたいと思います。</p>
<p>今回の目的はアヤメのデータセットの中から外れ値を検出することにあります。その上でアヤメの種類は3種類存在しています。つまり、3種類をまとめたデータから外れ値を検出することは有効でない可能性があります。3種類それぞれに特徴が存在しているわけですから、種類毎に外れ値を検出した方が有効的なのではないかと考えることができます。そこで、3種類のアヤメそれぞれに対して箱ひげ図と詳細な情報を出力してみます。先ほどの箱ひげ図と比較して見てください。まずはsetasaにの箱ひげ図について表示して行きます。</p><pre class="crayon-plain-tag">#[IN]:

#setosaが含まれるデータのみを抜き出す
setosa_df=df.groupby('species').get_group("setosa")

#setosaのデータに対する箱ひげ図を表示
setosa_df=get_box(setosa_df)</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/04/code3.png" alt="" /><br />
種類ごとに表示される前には現れていなかったpetal_lengthとpetal_widthにも外れ値が存在しています。このように注目するポイントを変えることで、あらたな外れ値が出現することもあります。データセットによってどのように注目点を変えるかは異なりますが、仮説を立てながらデータの味方を変えることは重要です。setosaについての四分位数についての情報も表示します。</p><pre class="crayon-plain-tag">#[IN]:

#setosaのデータに対する箱ひげ図の詳細を表示
setosa_df=box_Outlier(setosa_df)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

sepal_length
Q1は：4.8
Q3は：5.2
IQRは：0.40000000000000036
外れ値は↓
Series([], Name: sepal_length, dtype: float64)
*********************
sepal_width
Q1は：3.2
Q3は：3.6750000000000003
IQRは：0.4750000000000001
外れ値は↓
15    4.4
41    2.3
Name: sepal_width, dtype: float64
*********************
petal_length
Q1は：1.4
Q3は：1.5750000000000002
IQRは：0.17500000000000027
外れ値は↓
13    1.1
22    1.0
24    1.9
44    1.9
Name: petal_length, dtype: float64
*********************
petal_width
Q1は：0.2
Q3は：0.3
IQRは：0.09999999999999998
外れ値は↓
23    0.5
43    0.6
Name: petal_width, dtype: float64
*********************</pre><p>
setosaの外れ値はsepal_lengthに0個、sepal_widthに2個、petal_lengthに4個、petal_widthに2個検出されたことがわかりました。次はversicolorについての外れ値を検出して詳細を表示して見ます。</p><pre class="crayon-plain-tag">#[IN]:

#versicolorが含まれるデータのみを抜き出す
versicolor_df=df.groupby('species').get_group("versicolor")

#versicolorのデータに対する箱ひげ図を表示
versicolor_df=get_box(versicolor_df)</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/04/code4.png" alt="" /></p><pre class="crayon-plain-tag">#[IN]:

#versicolorのデータに対する箱ひげ図の詳細を表示
versicolor_df=box_Outlier(versicolor_df)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

sepal_length
Q1は：5.6
Q3は：6.3
IQRは：0.7000000000000002
外れ値は↓
Series([], Name: sepal_length, dtype: float64)
*********************
sepal_width
Q1は：2.525
Q3は：3.0
IQRは：0.4750000000000001
外れ値は↓
Series([], Name: sepal_width, dtype: float64)
*********************
petal_length
Q1は：4.0
Q3は：4.6
IQRは：0.5999999999999996
外れ値は↓
98    3.0
Name: petal_length, dtype: float64
*********************
petal_width
Q1は：1.2
Q3は：1.5
IQRは：0.30000000000000004
外れ値は↓
Series([], Name: petal_width, dtype: float64)
*********************</pre><p>
versicolorの外れ値はsepal_lengthに0個、sepal_widthに0個、petal_lengthに1個、petal_widthに0個検出されたことがわかりました。versicolorにはそれほど外れ値はないようです。次はvirginicaについての外れ値を検出して詳細を表示して見ます。</p><pre class="crayon-plain-tag">#[IN]:

#virginicaが含まれるデータのみを抜き出す
virginica_df=df.groupby('species').get_group("virginica")

#virginicaのデータに対する箱ひげ図を表示
virginica_df=get_box(virginica_df)</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/04/code5.png" alt="" /></p><pre class="crayon-plain-tag">#[IN]:

#virginicaのデータに対する箱ひげ図の詳細を表示
virginica_df=box_Outlier(virginica_df)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

sepal_length
Q1は：6.2250000000000005
Q3は：6.9
IQRは：0.6749999999999998
外れ値は↓
106    4.9
Name: sepal_length, dtype: float64
*********************
sepal_width
Q1は：2.8
Q3は：3.1750000000000003
IQRは：0.37500000000000044
外れ値は↓
117    3.8
119    2.2
131    3.8
Name: sepal_width, dtype: float64
*********************
petal_length
Q1は：5.1
Q3は：5.875000000000001
IQRは：0.7750000000000012
外れ値は↓
Series([], Name: petal_length, dtype: float64)
*********************
petal_width
Q1は：1.8
Q3は：2.3
IQRは：0.4999999999999998
外れ値は↓
Series([], Name: petal_width, dtype: float64)
*********************</pre><p>
virginicaの外れ値はsepal_lengthに1個、sepal_widthに3個、petal_lengthに0個、petal_widthに0個検出されたことがわかりました。箱ひげ図による外れ値検出は以上になります。アヤメのデータセットでは種類ごとに50個ずつのデータが存在します。これは機械学習の分野では決して多い量ではありません。この後、外れ値として検出された値を全て除外するのも一つの手ではありますが、全体の分布を見ながら検討する必要があることを忘れないでください。</p>
<h4>zスコア</h4>
<p>ここまでは箱ひげ図を用いて外れ値検出を行ってきました。ここからはzスコアを用いて外れ値検出を行います。zスコアは正規分布を過程としているため、アヤメの種類ごとにデータをヒストグラムで表示して見たいと思います。ヒストグラム表示のための関数をget_hist()として定義します。</p><pre class="crayon-plain-tag">#[IN]:

#ヒストグラムを作成するための関数を定義
def get_hist(input_df):
  #入力のコピーを作成
  output_df=input_df.copy()
  #表示する図のサイズを指定
  fig = plt.figure(figsize=(20,20))
  #詳細を表示するデータの列を指定
  num_list=["sepal_length","sepal_width","petal_length","petal_width"]
  #指定した列分繰り返す
  for i in range(len(num_list)):
    #1出力に複数の図を表示できるように設定
    plt.subplot(len(num_list), 4, i+1)
    #ヒストグラムの表示
    output_df[num_list[i]].plot.hist(bins=15)
  return output_df</pre><p>
setosa、versicolor、virginicaについてのヒストグラムを順番に表示します。</p><pre class="crayon-plain-tag">#[IN]:

#setosaのデータに対するヒストグラムを表示
setosa_df=get_hist(setosa_df)</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/04/code6.png" alt="" /></p><pre class="crayon-plain-tag">#[IN]:

#versicolorのデータに対するヒストグラムを表示
versicolor_df=get_hist(versicolor_df)</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/04/code7.png" alt="" /></p><pre class="crayon-plain-tag">#[IN]:

#virginicaのデータに対するヒストグラムを表示
virginica_df=get_hist(virginica_df)</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/04/code8.png" alt="" /><br />
それぞれのヒストグラムを表示することはできました。どのグラフも正規分布に完全に合致という訳ではないですが、極端に違う分布もないため、今回は分布を変化させずにそのままzスコアを求めて行きます。それぞれのデータの平均と標準偏差、そしてzスコアによって得られた外れ値を表示します。今回はzスコアの有意水準を2に設定します。</p><pre class="crayon-plain-tag">#[IN]:

#ヒストグラムを作成するための関数を定義
def get_zscore(input_df):
  #入力のコピーを作成
  output_df=input_df.copy()
  #詳細を表示するデータの列を指定
  num_list=["sepal_length","sepal_width","petal_length","petal_width"]
  #指定した列分繰り返す
  for i in range(len(num_list)):
    #列の平均を求める
    mean=output_df[num_list[i]].mean()
    #列の標準偏差を求める
    std=output_df[num_list[i]].std(ddof=0)
    #zスコアを求める
    zscore=(output_df[num_list[i]]-mean)/std
    #列名、平均、標準偏差、外れ値を表示
    print(str(num_list[i]))
    print("平均は："+str(mean))
    print("標準偏差は："+str(std))
    print("外れ値は↓")
    print(str(output_df[num_list[i]][(zscore&lt;-2)|(zscore&gt;2)]))
    print("*********************")
  return output_df</pre><p>
関数が定義できたので、setosaについての外れ値検出を行います。今回は有意水準が2なので、データ全体の95.44%から外れた値を外れ値にしています。</p><pre class="crayon-plain-tag">#[IN]:

#setosaのデータでのzスコアによる外れ値を表示
setosa_df=get_zscore(setosa_df)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

sepal_length
平均は：5.005999999999999
標準偏差は：0.348946987377739
外れ値は↓
13    4.3
14    5.8
Name: sepal_length, dtype: float64
*********************
sepal_width
平均は：3.428000000000001
標準偏差は：0.3752545802518604
外れ値は↓
15    4.4
33    4.2
41    2.3
Name: sepal_width, dtype: float64
*********************
petal_length
平均は：1.4620000000000002
標準偏差は：0.17191858538273286
外れ値は↓
13    1.1
22    1.0
24    1.9
44    1.9
Name: petal_length, dtype: float64
*********************
petal_width
平均は：0.2459999999999999
標準偏差は：0.10432641084595984
外れ値は↓
23    0.5
43    0.6
Name: petal_width, dtype: float64
*********************</pre><p>
setosaの外れ値はsepal_lengthに2個、sepal_widthに3個、petal_lengthに4個、petal_widthに2個検出されたことがわかりました。次はversicolorについての外れ値を検出して詳細を表示してみます。</p><pre class="crayon-plain-tag">#[IN]:

#versicolorのデータでのzスコアによる外れ値を表示
versicolor_df=get_zscore(versicolor_df)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

sepal_length
平均は：5.936
標準偏差は：0.5109833656783752
外れ値は↓
50    7.0
57    4.9
Name: sepal_length, dtype: float64
*********************
sepal_width
平均は：2.7700000000000005
標準偏差は：0.31064449134018135
外れ値は↓
60    2.0
85    3.4
Name: sepal_width, dtype: float64
*********************
petal_length
平均は：4.26
標準偏差は：0.4651881339845204
外れ値は↓
57    3.3
93    3.3
98    3.0
Name: petal_length, dtype: float64
*********************
petal_width
平均は：1.3259999999999998
標準偏差は：0.19576516544063702
外れ値は↓
70    1.8
Name: petal_width, dtype: float64
*********************</pre><p>
versicolorの外れ値はsepal_lengthに2個、sepal_widthに2個、petal_lengthに3個、petal_widthに1個検出されたことがわかりました。次はvirginicaについての外れ値を検出して詳細を表示してみます。</p><pre class="crayon-plain-tag">#[IN]:

#virginicaのデータでのzスコアによる外れ値を表示
virginica_df=get_zscore(virginica_df)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

sepal_length
平均は：6.587999999999998
標準偏差は：0.6294886813914925
外れ値は↓
106    4.9
131    7.9
Name: sepal_length, dtype: float64
*********************
sepal_width
平均は：2.9739999999999998
標準偏差は：0.319255383666431
外れ値は↓
117    3.8
119    2.2
131    3.8
Name: sepal_width, dtype: float64
*********************
petal_length
平均は：5.552
標準偏差は：0.5463478745268441
外れ値は↓
117    6.7
118    6.9
122    6.7
Name: petal_length, dtype: float64
*********************
petal_width
平均は：2.026
標準偏差は：0.2718896835115301
外れ値は↓
134    1.4
Name: petal_width, dtype: float64
*********************</pre><p>
virginicaの外れ値はsepal_lengthに2個、sepal_widthに3個、petal_lengthに3個、petal_widthに1個検出されたことがわかりました。</p>
<p>3種類とも外れ値の検出数が箱ひげ図の時よりも多いことが分かるかと思います。今回は有意水準が2でしたが、値によってはもう少し厳しくしたり、緩くしたりすることができます。外れ値の検出方法は様々ですが、最終的には自分の判断で有意水準を決める必要があります。そのため、それらを判断するためのドメイン知識というのが重要になってくるのです。</p>
<h3>まとめ</h3>
<p>今回は外れ値について解説と実装を行ってきました。本稿を読んでいただければご理解いただけるように、機械学習の勉強は外れ値一つとってもこれだけの量の知識が広がっています。数日勉強して身につくレベルではありませんが、だからこそ機械学習エンジニアが重宝されているとも言えます。今回の記事が本稿を読まれている方々の助けになれば幸いです。</p>
<p>codexaでは機械学習初学者に向けたコースを複数提供しています。</p>
<p><strong><span class="su-highlight"> 機械学習 準備編 無料講座  </span></strong></p>
<ul>
<li><a href="https://www.codexa.net/numpy/">Numpy 入門</a></li>
<li><a href="https://www.codexa.net/matplotlib/">Matplotlib 入門</a></li>
<li><a href="https://www.codexa.net/pandas/">Pandas 入門</a></li>
<li><a href="https://www.codexa.net/linear-basics/">線形代数 入門</a></li>
<li><a href="https://www.codexa.net/statistics-for-machine-learning-first/">統計入門（前編）</a></li>
<li><a href="https://www.codexa.net/statistics-for-machine-learning-second/">統計入門（後編）</a></li>
<li><a href="https://www.codexa.net/linear-regression-for-beginner/">線形回帰 入門</a></li>
</ul>
<p>是非、皆様のご受講をお待ちしております。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.codexa.net/python-outlier/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>欠損値とは？Pythonを使って欠損値の処理方法と実装を徹底解説【機械学習 入門編】</title>
		<link>https://www.codexa.net/missing_value_python/</link>
					<comments>https://www.codexa.net/missing_value_python/#respond</comments>
		
		<dc:creator><![CDATA[codexaチーム]]></dc:creator>
		<pubDate>Mon, 17 May 2021 06:00:11 +0000</pubDate>
				<category><![CDATA[機械学習]]></category>
		<guid isPermaLink="false">https://www.codexa.net/?p=2916</guid>

					<description><![CDATA[欠損値とは、あるデータ内の変数において存在しない値を指します。本稿では欠損値の概要や種類、またPythonを使った欠損値の処理方法を実装サンプルコードを含めて徹底解説しています。]]></description>
										<content:encoded><![CDATA[<p>突然ですが、皆さんはアンケートの回答を求められた経験はありませんか？ 道端で声を掛けられる場合もあれば、飲食店の机の上に置いてある場合もあります。例えば皆さんが機械学習の勉強会に参加していたとします。以下の項目を聞かれた場合、皆さんは何項目記入しますか？</p>
<p>■セミナー来場者アンケート<br />
1. お名前<br />
2. 性別<br />
3. 年齢<br />
4. 住所<br />
5. どのように今回のセミナーを知りましたか？<br />
6. 本日の感想</p>
<p><img src="http://4.bp.blogspot.com/-gc_pG0Lr7GE/VJ6XKoPXnZI/AAAAAAAAqGo/1Hp3nOZo9CM/s180-c/test_print_mondaiyoushi.png" /><br />
（引用：<a href="https://www.irasutoya.com/search?q=%E3%83%86%E3%82%B9%E3%83%88">いらすとや</a>）</p>
<p>人によって記入する項目は違うと思いますが、来場者全員がアンケートをすべて答えてくれるとは限りません。しかし、もしあなたがセミナーの主催企業の社員であり、このアンケートを利用してデータ分析を行いたいと思っていたとすればデータに穴があることは非常に大きな問題です。未記入部分のデータの扱い方を間違えた場合、データ分析がうまくいかない可能性があります。</p>
<p>機械学習の分野でも、このアンケートのようにデータが完全には揃わない場合があります。特に現実に存在するデータでは整っていない場合の方が多いです。アンケートの例で言えば、未記入の項目があったり、回収時に破れてしまったりして、データの一部が欠損してしまうことが考えられます。データ分析を行う以上はこの欠損した値に対応しなければなりません。本稿では機械学習分野で出現する欠損値の解説と、Pythonによる欠損値の対処法を実装していきます。</p>
<div class="su-note"  style="border-color:#dadada;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;"><div class="su-note-inner su-u-clearfix su-u-trim" style="background-color:#f4f4f4;border-color:#ffffff;color:#333333;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;">
<p><strong>前提理解</strong></p>
<p>これから欠損値を説明していく前に理解しておいていただきたいことをお伝えします。「欠損値」という言葉自体は比較的認知されている用語ですが、その性質を正しく理解するにはある程度の数学的知識が必要になります。特に初学者の方にとっては高いハードルとなる可能性があります。現に、欠損値に特化した内容の本が出版されているほどです。加えて、用意されたデータセットに対し、統計学的に正しいと理論づけられる欠損値処理を実装する事はより難しいと言えます。そのため、本稿では欠損値については基礎的な部分を中心に説明・実装していきます。本稿を読んだ後で、より詳細に欠損値について理解されたい方は以下の本を参考にしながら勉強してみてください。（参考：<a href="https://www.amazon.co.jp/%E6%AC%A0%E6%B8%AC%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AE%E7%B5%B1%E8%A8%88%E8%A7%A3%E6%9E%90-%E7%B5%B1%E8%A8%88%E8%A7%A3%E6%9E%90%E3%82%B9%E3%82%BF%E3%83%B3%E3%83%80%E3%83%BC%E3%83%89-%E9%98%BF%E9%83%A8-%E8%B2%B4%E8%A1%8C/dp/4254128592/ref=sr_1_7?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&amp;dchild=1&amp;keywords=%E6%AC%A0%E6%90%8D%E5%80%A4&amp;qid=1615775269&amp;sr=8-7" target="_blank" rel="noopener">欠測データの統計解析 (統計解析スタンダード)</a>・<a href="https://www.amazon.co.jp/%E6%AC%A0%E6%B8%AC%E3%83%87%E3%83%BC%E3%82%BF%E5%87%A6%E7%90%86-R%E3%81%AB%E3%82%88%E3%82%8B%E5%8D%98%E4%B8%80%E4%BB%A3%E5%85%A5%E6%B3%95%E3%81%A8%E5%A4%9A%E9%87%8D%E4%BB%A3%E5%85%A5%E6%B3%95-%E7%B5%B1%E8%A8%88%E5%AD%A6One-%E9%AB%98%E6%A9%8B-%E5%B0%86%E5%AE%9C/dp/4320112563/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&amp;dchild=1&amp;keywords=%E6%AC%A0%E6%90%8D%E5%80%A4&amp;qid=1615775269&amp;sr=8-1" target="_blank" rel="noopener">欠測データ処理: Rによる単一代入法と多重代入法</a>）</p>
</div></div>
<h3>欠損値とは？</h3>
<p>あるデータ内の変数において、存在しない値を欠損値（missing value）と言います。統計学の分野では以前から存在した欠損値ですが、データ分析において頭を悩ませる原因の1つであることは間違いありません。しかし、欠損値が存在している以上私たちは何らかの手段を取らなければなりません。特にデータ分析の手法の中には欠損値を扱えないものも存在し、そのままにしておくのはリスクがあります。欠損値の性質はデータセットによっても変わるため、正解の対処法を記載することはできませんが、比較的重要なポイントとしては以下の2点があると筆者は考えます。</p>
<p>1つ目は「なぜ欠損が発生したか」ということです。欠損の原因が判明すれば、その部分のデータを補完できる方法を考えることができます。欠損値の部分を正しい値で補完できることが望ましいことは間違いありません。しかし、現実問題として、欠損値を正確なデータで埋めることは困難な場合が多いです。アンケートの記入漏れを一つ見つけるたびに、回答者の方に再記入をお願いしていたのではデータ収集自体が終了しません。それでも欠損の理由を調べることでその後の対処法に活かせる可能性は大きくなります。</p>
<p>2つ目は「どのように欠損値に対処するか」ということです。発生してしまった欠損値に対し、本来のデータからの誤差が少しでも小さくなるように欠損値を補完することが求められます。欠損値を無視したり、独自の値で埋めたり、それ自体を予測したりと手法は様々ですが、根拠のある対処を行わなければなりません。ここで難しいのは欠損値の対処を間違えると本来のデータ自体が歪んでしまうという事です。たとえ素晴らしいモデルを利用したとしても歪んだデータから導き出された予測は精度が落ちかねません。</p>
<h3>欠損値の種類</h3>
<p>欠損値には大きく分けて3つの種類があります。それぞれの欠損値が特徴を持っているため、その部分を意識しながら見ていきましょう。種類ごとに理解するのは手間かもしれませんが、欠損値に対する対処法は種類によって良し悪しがあるため、しっかりと抑える必要があります。欠損値の種類について、以下の表を参考に解説していきます。</p>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/03/欠損値とは.png" alt="" /></p>
<h4>MCAR（Missing Completely At Random）</h4>
<p>一つ目はMCARです。MCARは欠損値がランダムに発生している場合を指します。完全にランダムに発生するため、メカニズムの理解自体は簡単です。しかし、欠損値がランダムであるという証明が難しいために、欠損値の発生原因が分かりにくい場合もあります。それでも、欠損が偏っていないため他の2つと比べると比較的対処しやすいという特徴があります。</p>
<p>上の表をみても分かるように、ランダムに欠損が存在しています。今回は分かりやすくするために8行の表を用いていますが、本来のデータセットはもっと膨大な行が存在するので、欠損値の確認はしっかりと行う必要があります。</p>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/03/MCAR.png" alt="" /></p>
<h4>MAR（Missing At Random）</h4>
<p>MARは欠損値の有無が別の変数に依存している場合を指します。欠損値が存在する変数だけを見たときには一見するとランダムに欠損が発生しているように見えます。MCARとは異なり、依存先の変数に注意を払いながら対処しなかればなりません。そのため、欠損値処理もMCARに比べて少々複雑になります。</p>
<p>上の表をみても分かるように、欠損値が発生している行がクラスBに集中しています。そのため、「クラスBの生徒はテストを受けなかったのではないか」などの予測が立てられます。こういった予測を考慮した上で欠損値に対応していく必要があります。今回のクラスのような欠損の有無と相関をもつような変数を補助変数 (auxiliary variable) と言います。注意していただきたいのは、MARはあくまで欠損値が他の変数に依存しているという点です。今回のように他の変数に対応した行全てが欠損していなければならないといった制約はありません。</p>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/03/MAR.png" alt="" /></p>
<h4>MNAR（Missing Not At Random）</h4>
<p>MNARは欠損値の有無がそのデータに依存している場合を指します。依存が自身の数値内容によって影響を受けているため、予測を行うことが難しいという特徴があります。特にこのMNARにはまだ確実な対応手法は存在しません。MNAR自体は状態ではMARとは違いますが、仮にこの欠損値を説明できるような変数を用意でき、欠損値が存在する部分とその変数との依存が解消された場合にはMARになります。</p>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/03/MNAR.png" alt="" /></p>
<p>上の表をみても分かるように、欠損値が発生している行が数学の点数が低い方に集中しています。そのため、「点数の低い生徒は記録されなかったのではないか」などの予測が立てられます。他にも、点数の差はありますが、80点までは欠損が発生していないため、「80点未満は不合格な試験であり、不合格者の点数は発表されないのではないか」などの予測を立てることもできます。今回はデータ数が少ないため、正確な理由は不明ですが、欠損が発生しているというそのものに意味がある可能性もあるのでデータをよく確認しながら対処を考える必要があります。</p>
<h3>欠損値への対処法</h3>
<p>本節では、欠損値への対処法について記載していきます。前章でも述べたように、まずは「なぜ欠損値が発生したか」を知る必要があります。それを知った上で、本来のデータで欠損値を埋めることができれば正しい分析・予測ができるでしょう。しかし、欠損値の発生原因を特定できなかったり、データ分析コンペティションのルールで禁止されていたりなど、本来のデータで埋められないケースは珍しくありません。</p>
<p>欠損値を本来のデータで埋めることが難しい場合、欠損値に対処することが求められます。先にも述べたように、欠損値の正しい対処はデータセットや欠損値にも影響されるため、絶対に正しい対処法というのは存在しません。また、数学的に欠損値を補完すれば、どうしても誤差が発生してしまいます。これらを理解した上で基本的な対処法について見ていきましょう。</p>
<h4>欠損値をそのまま扱う</h4>
<p>機械学習のモデルによっては欠損値をそのまま扱うこともできます。当然、欠損値が存在したままでは使用できないモデルも存在するため、注意が必要です。欠損値をそのまま扱えるモデルの代表例としてはLightGBMなどがあります。データに対しての理解を疎かにして適当に値を埋めるよりも、欠損値のまま扱った方が精度が高まる場合もあります。LightGBMについてはAIマガジンでも<a href="https://www.codexa.net/lightgbm-beginner/">LightGBM 徹底入門 – LightGBMの使い方や仕組み、XGBoostとの違いについて</a>で取り上げています。</p>
<h4>欠損値を削除する</h4>
<p>欠損値を削除する手法について説明します。欠損値を扱えないモデルを使用する際には初期で検討されやすい手法ですので確実に抑えましょう。</p>
<h5>リストワイズ法（List-Wise Deletion）</h5>
<p>リストワイズ法は欠損値が含まれているサンプルデータ自体を削除してしまう方法です。欠損値の対処法の中でもイメージがつきやすい方だと思います。欠損値を削除することによって、データセット自体を欠損のない形に変換することができるため、その後モデルなどへの入力が容易になるという利点があります。</p>
<p>MCARでは欠損がランダムに発生しているため、データが歪む事はありません。しかし、欠損値が多いとデータ数自体が減少するため、データ全体がもつ情報量が減少してしまいます。また、MARやMNARでは欠損値の発生自体がランダムではないため、リストワイズ法で削除すると、データが歪む可能性があるため、注意が必要です。</p>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/03/リストワイズ法.png" alt="" /></p>
<p>上の表を見ても分かるように欠損値が存在する行が削除されたと思います。理解しやすい手法ですが、この手法は訓練データにのみ使用できることに注意してください。機械学習では主に訓練データ（検証用データ含む）とテストデータの2種類を使用します。訓練用データの削除は「削除したデータは学習に使用しない」と捉えることができます。しかし、テストデータは予測しなければならないデータであるため、削除を行なった場合は「削除したデータは予測できない」ということになります。これは欠損値を持つサンプルに対する予測を放棄することになり、望ましくありません。そのため、リストワイズ法を使用した場合はテストデータをどのように処理するかまでしっかりと検討する必要があります。</p>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/03/リストワイズ法_のデメリット.png" alt="" /></p>
<h5>ペアワイズ法（Pairwize Deletion）</h5>
<p>ペアワイズ法は個々の分析（2変数間の相関）などを行う際に、必要な変数内だけで欠損しているサンプルデータのみを削除する手法です。リストワイズ法のように極端にデータ数が減少する事はなく、削除されるデータ数が必要最低限で済みます。しかし、機械学習の分野においては多変量から予測を行うことがほとんどであることや、変数によって欠損の実装を行わなければならないことから、リストワイズ法の方が比較的よく使われます。</p>
<h4>欠損値を補完する</h4>
<p>最後に欠損値の補完方法について説明していきます。本稿の中では基本的な概要を抑え、実際の補完に関しては実データやコンペティションなどで経験を積んでいきましょう。</p>
<h5>単一代入法（Single Imputation Method）</h5>
<p>単一代入法は欠損値1つに対して1つの値で補完する手法です。実際には単一代入法にも複数の種類が存在します。最も初歩的なものは欠損値を同じ変数のその他の数値の平均値で補完する手法です。他にも多変数から回帰モデルを用いて欠損値を予測する回帰代入法なども存在します。単一代入法はデータ数を削減することもありません。実装も比較的簡単なため、データセットに合わせながら多変数のグループごとの平均を代入するなどの工夫を行う場合が多いです。</p>
<p>しかし、単一代入法を使用しても、欠損値を正しく埋められない場合も存在します。リストワイズ法であれば、欠損部分のサンプルデータは削除されるため、残ったデータは最初から存在したものだけになります。しかし、単一代入法では少なからず誤差のあるデータを代入することになるため、相関が異なったりするケースがあるので注意が必要です。</p>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/03/単一代入法.png" alt="" /></p>
<h5>多重代入法（Multiple Imputation Method）</h5>
<p>多重代入法は欠損値を代入したデータを複数個用意し、それぞれのデータに対して分析を行い、最終的にそれらの結果を統合して欠損値を補完する手法です。統計学的にはMCARやMARに対しては有効的な手法として用いられています。単一代入法では1つの値を代入した際のデータの歪みを考慮できないというデメリットがあります。多重代入法では欠損値処理の不安定さも結果に含みます。多重代入法のPythonによる実装は現在sickit-learnが実験的に提供しています。参考にしてみてください。（参考：<a href="https://scikit-learn.org/stable/modules/generated/sklearn.impute.IterativeImputer.html" target="_blank" rel="noopener">sklearn.impute.IterativeImputer</a>）</p>
<h5>完全情報最尤推定法（Full Maximum Likelihood Method; FIML）</h5>
<p>完全情報最尤推定法は欠損によって尤度関数を個別に定義し、こちらもMCARやMARに対して有効的な手法として用いられています。全体の尤度を最大化するものです。しかし、最尤推定を理解しながらPythonでの実装を行う事は初学者にとっては難易度が高いため、本稿では紹介のみにとどめたいと思います。</p>
<p>ここまで、欠損値の種類や欠損値処理を説明してきましたが、本稿での内容をより詳細に、数学的に理解されたい方は以下の記事を参考にしてみてください。数学的な基礎知識は機械学習を勉強する上で重要な要素です。（参考：<a href="https://koumurayama.com/koujapanese/missing_data.pdf" target="_blank" rel="noopener">欠損データ分析 (missing data analysis)-完全情報最尤推定法と多重代入法-村山 航</a>）</p>
<p>本稿の後半では主に単一代入法に焦点を当てて欠損値処理を実装していきます。単一代入法の手法は参照できる記事も多く、実装方法も容易なため、比較的取り組みやすいです。データセットにもよりますが、初学者の方は単一代入法とリストワイズ手法を上手く組み合わせながら欠損値を処理できるようにすることをオススメします。（参考：<a href="https://ja.wikipedia.org/wiki/%E4%BB%A3%E5%85%A5%E6%B3%95_(%E7%B5%B1%E8%A8%88%E5%AD%A6" target="_blank" rel="noopener">代入法 (統計学)</a>）</p>
<h3>欠損値処理の実装</h3>
<p>本節では、欠損値処理をGoogle Colabを用いて実装していきます。本稿は2021年5月時点でコードの実行確認を行いましたので、Google Colabのデフォルトのバージョンが変更されない限り、ライブラリをそのままインポートすれば同じように実装可能です。是非、ご自身でも実装してみてください。（参考：<a href="https://www.codexa.net/how-to-use-google-colaboratory/">Google Colabの知っておくべき使い方 – Google Colaboratoryのメリット・デメリットや基本操作のまとめ</a>）</p>
<p>まずは必要なライブラリをインポートしていきます。</p><pre class="crayon-plain-tag">#[IN]:

#ライブラリの読み込み
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import copy
import warnings
warnings.filterwarnings('ignore')</pre><p>
データセットにはkaggleのチュートリアルで最も有名なTitanicのデータセットを利用します。<a href="https://www.kaggle.com/c/titanic" target="_blank" rel="noopener">こちら</a>のサイトからデータセットをダウンロードしてGoogle Colab上にフォルダにtrain.csvをアップロードして下さい。アップロードが完了したらデータセットを読み込んで、表示します。</p>
<p>titanicの特徴量の説明は以下の通りです。AIマガジンでも以前に特徴量の作成からsubmitまで行なった<a href="https://www.codexa.net/kaggle-titanic-beginner/">【Kaggle初心者入門編】タイタニック号で生き残るのは誰？</a>を掲載しています。今回はsubmitまでは行わないため、興味がある方はこちらも確認してみてください。</p>
<ul>
<li> PassengerId – 乗客識別ユニークID</li>
<li>Survived – 生存フラグ（0=死亡、1=生存）</li>
<li>Pclass – チケットクラス</li>
<li>Name – 乗客の名前</li>
<li>Sex – 性別（male=男性、female＝女性）</li>
<li>Age – 年齢</li>
<li>SibSp – タイタニックに同乗している兄弟/配偶者の数</li>
<li>parch – タイタニックに同乗している親/子供の数</li>
<li>ticket – チケット番号</li>
<li>fare – 料金</li>
<li>cabin – 客室番号</li>
<li>Embarked – 出港地（タイタニックへ乗った港）</li>
</ul>
<pre class="crayon-plain-tag">#[IN]:

#titanicデータセットを読み込んで、一部を表示
df = pd.read_csv('train.csv')
df.head()</pre>
<img src="https://www.codexa.net/wp-content/uploads/2021/03/スクリーンショット-2021-03-17-17.49.43.png" alt="" /><br />
データセットの最初5行が表示されました。それぞれの特徴量の欠損値の数を確認してみましょう。<br />
<pre class="crayon-plain-tag">#[IN]:

#欠損値の数を表示
print(df.isnull().sum())</pre>
<pre class="crayon-plain-tag">#[OUT]:

PassengerId 0
Survived 0
Pclass 0
Name 0
Sex 0
Age 177
SibSp 0
Parch 0
Ticket 0
Fare 0
Cabin 687
Embarked 2
dtype: int64</pre>
「Age」と「Cabin」に欠損が多いようです。今回は欠損値処理に焦点を当てるため、特徴量の抽出とカテゴリカルデータを数値に変換する処理は先に行ってしまいます。「Embarked」に関しては欠損が2つのみなので今回詳しくは触れません。「Embarked」の最頻値は「S」であるため、ここでは「S」で補完します。<br />
<pre class="crayon-plain-tag">#[IN]:

#PassengerID、Name、Ticket以外を抽出する
df = df[["Survived","Pclass","Sex","Age","SibSp","Parch","Fare","Cabin","Embarked"]]
#Sexで男性を1、女性を0に置換する
df["Sex"][df["Sex"] == "male"] = 0
df["Sex"][df["Sex"] == "female"] = 1
#Embarkedの欠損値をSで補完する
df["Embarked"] = df["Embarked"].fillna("S")
#EmbarkedをSを0、Cを1、Qを2に置換する
df["Embarked"][df["Embarked"] == "S" ] = 0
df["Embarked"][df["Embarked"] == "C" ] = 1
df["Embarked"][df["Embarked"] == "Q" ] = 2</pre>
今回は欠損値処理ごとの精度を比較したいため、891行存在するtrainデータを訓練用と検証用に6：4で分割していきます。分割にはsickit-learnが提供するtrain_test_split()を使用します。<br />
<pre class="crayon-plain-tag">#[IN]:

#trainとtestに6:4の割合で分割してデータの長さを表示する
train,test=train_test_split(df,test_size=0.4,random_state=0)

print("訓練用のデータは"+str(len(train))+"です")
print("検証用のデータは"+str(len(test))+"です")</pre>
<pre class="crayon-plain-tag">#[OUT]:

訓練用のデータは534です
検証用のデータは357です</pre>
訓練用と検証用にデータを分割できました。次に、それぞれのデータ内にどれだけの欠損が存在しているか確認します。<br />
<pre class="crayon-plain-tag">#[IN]:

#欠損値の数を表示
print(train.isnull().sum())
print(test.isnull().sum())</pre>
<pre class="crayon-plain-tag">#[OUT]:

Survived 0
Pclass 0
Sex 0
Age 103
SibSp 0
Parch 0
Fare 0
Cabin 407
Embarked 0
dtype: int64
Survived 0
Pclass 0
Sex 0
Age 74
SibSp 0
Parch 0
Fare 0
Cabin 280
Embarked 0
dtype: int64</pre>
欠損値を確認すると、訓練用データは「Age」に103、「Cabin」に407存在しています。検証用データは「Age」に74、「Cabin」に280存在しています。「Age」と「Cabin」の欠損値をどうするかが重要になりそうです。欠損が存在しない特徴量に関してはこれ以上手を加えずに予測に使用していきます。</p>
<h4>リストワイズ法による欠損値削除</h4>
<p>まずは、「欠損値を処理するよりも削除する方が楽」という考えの元、リストワイズ法を試してみます。pandasのdropnaメソッドを使用して、訓練用データ内の欠損がある行を全て削除します。欠損値処理ごとの精度を比較したいため、copyメソッドを使用して複製を行います。</p><pre class="crayon-plain-tag">#[IN]:

#訓練用データ内の欠損がある行を全て削除して表示する
train_drop=train.copy()
train_drop=train_drop.dropna()
len(train_drop)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

117</pre><p>
訓練用データ数が117になりました。もともとは534であったため、大部分のデータが削除されてしまったことが分かります。検証用データには357ものデータ数が存在しているので、今回の訓練用データの方が検証用データよりも少なくなっています。加えて検証用データの欠損値をどのように処理するかを考えなければなりません。先にも述べたようにテスト用データ（今回の検証用データ）は削除することができません。そのため、欠損値を扱えるモデルを使用するなどして対応する必要があります。数行であれば比較的影響を少なく抑えることができますが、今回はあまりに削除したデータが多いため、リストワイズのみを使用した欠損値処理はここまでにします。</p>
<p>titanicのデータセットの「Cabin」は欠損値が多いのに加えて、カテゴリカルデータであるため、とても扱いづらいです。そこで、「Cabin」を削除し、「Age」の欠損値処理を中心に行っていきたいと思います。このようにあまりに欠損値が多い特徴量に関しては特徴量から外すという選択肢を取ることもあります。</p><pre class="crayon-plain-tag">#[IN]:

#訓練用データと検証用データからCabinの列を削除して先頭を表示する
train=train.drop("Cabin",axis=1)
test=test.drop("Cabin",axis=1)
print(train.head())
print(test.head())</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/03/スクリーンショット-2021-03-17-18.56.47.png" alt="" /><br />
訓練用データ、検証用データから共に「Cabin」を削除することができました。次からは単一代入法を使用して欠損値を補完していきます。</p>
<h4>平均値による補完</h4>
<p>「平均値で欠損値を補完すれば精度が少しは上がる」という予測のもと、「Age」をまずは平均値で補完していきます。</p><pre class="crayon-plain-tag">#[IN]:

#訓練用データと検証用データのAgeを平均値で補完する
train_mean=train.copy()
train_mean["Age"] = train_mean["Age"].fillna(train_mean["Age"].mean())
test_mean=test.copy()
test_mean["Age"] = test_mean["Age"].fillna(test_mean["Age"].mean())
print(train_mean.isnull().sum())
print(test_mean.isnull().sum())</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

Survived 0
Pclass 0
Sex 0
Age 0
SibSp 0
Parch 0
Fare 0
Embarked 0
dtype: int64
Survived 0
Pclass 0
Sex 0
Age 0
SibSp 0
Parch 0
Fare 0
Embarked 0
dtype: int64</pre><p>
欠損値が補完できていることが確認できています。予測のための準備が整ったので、実際にモデルを構築していきます。今回、モデルはロジスティック回帰を使用します。複数回使用するため、関数で定義し、返り値には検証用データに対する予測値と正解のラベルを設定します。</p>
<p>（ロジスティック回帰の詳細はcodexaの<a href="https://www.codexa.net/">実践 ロジスティック回帰</a>をご参考ください。）</p><pre class="crayon-plain-tag">#[IN]:

#ロジスティック回帰をインポートする
from sklearn.linear_model import LogisticRegression
def get_logi(train,test):
  #訓練用データから目的変数と説明変数に分割する
  y_train = train["Survived"].values
  x_train = train.drop('Survived', axis=1).values
  #検証用データから目的変数と説明変数に分割する
  y_test = test["Survived"].values
  x_test = test.drop('Survived', axis=1).values
  #ロジスティック回帰
  my_logi = LogisticRegression(random_state=0)
  my_logi = my_logi.fit(x_train, y_train)
  #検証用データで予測を求める
  my_prediction = my_logi.predict(x_test)
  return my_prediction,y_test</pre><p>
関数が定義できたので、訓練データと検証用データを与えていきます。その後、返り値を利用して精度を求めます。精度にはscikit-learnのaccuracy_score()を用います。</p><pre class="crayon-plain-tag">#[IN]:

#学習、予測、精度を求める
my_prediction_a,test_target_a = get_logi(train_mean,test_mean)
accuracy_score(test_target_a, my_prediction_a)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

0.7927170868347339</pre><p>
精度は約0.79になりました。今は通常の平均値を利用して欠損値を補完しましたが、もう少し詳しくデータを眺めて補完方法を考えてみましょう。</p>
<h4>PclassごとのAgeの平均値補完</h4>
<p>少しAgeについてデータを見ながら考えて見ましょう。どのデータ分析でも変数同士の関連性はとても重要です。まずは訓練用データ内の相関を見てみましょう。</p><pre class="crayon-plain-tag">#[IN]:

#訓練用データの相関を表示する
train.corr()</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/03/スクリーンショット-2021-03-17-18.07.57.png" alt="" /><br />
表をみてみると「Pclass」が「Age」と最も相関がありそうです。そのため、Pclassに対するその他変数の平均を調べてみます。ちなみにPclassの説明は以下の通りになります。</p>
<p>* 1 = 上層クラス（お金持ち）<br />
* 2 = 中級クラス（一般階級）<br />
* 3 = 下層クラス（労働階級）</p><pre class="crayon-plain-tag">#[IN]:

#Pclassごとの平均を表示する
train.groupby('Pclass').mean()</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/03/スクリーンショット-2021-03-17-18.09.10.png" alt="" /><br />
Pclassごとの平均をみてみるとランクが上がるに連れてAgeの平均が上がっています。そのため、「お金持ちの方が年齢が高い傾向にあるのではないか」という予測のもと、「Age」の欠損値を「Pclass」ごとの平均値で補完してみます。そして先程作成したget_logi()関数を使用して予測値を求め、精度を表示します。</p><pre class="crayon-plain-tag">#[IN]:

#訓練用データと検証用データをPclassごとの平均値で補完する
train_Pclass_mean=train.copy()
train_Pclass_mean["Age"]= train_Pclass_mean.groupby('Pclass').transform(lambda x: x.fillna(x.mean()))['Age']
test_Pclass_mean=test.copy()
test_Pclass_mean["Age"]= test_Pclass_mean.groupby('Pclass').transform(lambda x: x.fillna(x.mean()))['Age']</pre><p>
</p><pre class="crayon-plain-tag">#[IN]:

#学習、予測、精度を求める
my_prediction_b,test_target_b=get_logi(train_Pclass_mean,test_Pclass_mean)
accuracy_score(test_target_b, my_prediction_b)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

0.803921568627451</pre><p>
こちらも精度は約0.8になりました。通常の平均値と比べてもあまり精度はさほど変わりませんでした。</p>
<h4>欠損値を予測して補完する</h4>
<p>最後に「Age自体を予測すれば精度が上がるのでは」という予測のもと、欠損値自体を予測したいと思います。</p><pre class="crayon-plain-tag">#[IN]:

#訓練データと検証用データを複製する
train_age_predict=train.copy()
test_age_predict=test.copy()</pre><p>
ランダムフォレストによる実装を行うための関数を定義します。返り値には予測された欠損値が代入された訓練用データと検証用データが設定されています。欠損値の処理を完了した値をそのままget_logi()関数に入力し、予測値を求め、精度を用事します。</p>
<p>ランダムフォレストに関してはcodexaの<a href="https://www.codexa.net/">決定木とランダムフォレスト</a>の方でも提供していますのでより詳しく勉強されたい方は参照してみてください。</p><pre class="crayon-plain-tag">#[IN]:

#ランダムフォレストをインポートする
from sklearn.ensemble import RandomForestRegressor
def age_predict(train,test):
  #訓練用データをAgeの欠損で分ける
  x_train = train.dropna()
  y_train = train[train.isnull().any(axis=1)]
  #検証用データをAgeの欠損で分ける
  x_test = test.dropna()
  y_test = test[test.isnull().any(axis=1)]
  #欠損がないデータを使用してランダムフォレストを学習する
  model_train = RandomForestRegressor(random_state=1).fit(x_train.drop(["Age","Survived"],axis=1), x_train["Age"])
  model_test = RandomForestRegressor(random_state=1).fit(x_test.drop(["Age","Survived"],axis=1), x_test["Age"])
  #Ageの欠損値を予測して補完する
  y_train["Age"] = model_train.predict(y_train.drop(["Age","Survived"],axis=1))
  y_test["Age"] = model_test.predict(y_test.drop(["Age","Survived"],axis=1))
  #結合し直す
  train = pd.concat([x_train,y_train]).sort_index()
  test = pd.concat([x_test,y_test]).sort_index()
  return train, test</pre><p>
</p><pre class="crayon-plain-tag">#[IN]:

#Ageの欠損値を予測する
train_age_predict,test_age_predict = age_predict(train_age_predict,test_age_predict)

#学習、予測、精度を求める
my_prediction_c,test_target_c = get_logi(train_age_predict,test_age_predict)
accuracy_score(test_target_c, my_prediction_c)</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

0.8067226890756303</pre><p>
<table>
<tbody>
<tr>
<td style="text-align: left;">平均値補完</td>
<td style="text-align: left;">0.7927170868347339</td>
</tr>
<tr>
<td>Pclassごとの平均値補完</td>
<td>0.803921568627451</td>
</tr>
<tr>
<td style="text-align: left;">ランダムフォレスト補完</td>
<td style="text-align: left;">0.8067226890756303</td>
</tr>
</tbody>
</table>
<p style="text-align: left;">精度は約0.8になりました。他2つの精度と比較しても大きな変化はありませんでした。「Age」の欠損値に対して3つの手法を適応して補完してきましたが、精度に大きな影響を与えることはできませんでした。「Age」という変数自体が「Survived」との相関があまり高くないことも影響していると考えられます。今回は、欠損値の補完方法を説明するのが目的だったため、精度にはこだわりませんでしたが、みなさんは自身が分析するデータセットに合わせた欠損値処理を行い精度向上を目指してみてください。</p>
<p style="text-align: left;">データ分析を進めていく上で、欠損値との付き合い方は非常に重要になります。色々なデータセットにチャレンジしながら、欠損値の処理を練習してみて下さい。AIマガジンでも<a href="https://www.codexa.net/ml-dataset-list/">【24個掲載】機械学習で使えるデータセット一挙勢揃い！</a>でデータセットを紹介しています。</p>
<h3 style="text-align: left;">まとめ</h3>
<p style="text-align: left;">ここまで欠損値に対しての解説と実装を行ってきました。冒頭にも述べたように欠損値はデータセットによって処理方法が異なります。今回紹介した欠損値処理の方法は多くのデータセットで用いやすい汎用的な物を選びました。本稿で取り上げた手法が、皆様が欠損値処理を行うときに少しでも参考になれば幸いです。</p>
<p style="text-align: left;">codexaでは初学者や独学者向けの機械学習のコースを提供しています。今回のような統計的な内容から、ライブラリまで様々なコースを取り上げています。</p>
<p>・<a href="https://www.codexa.net/numpy/">Numpy 入門</a>（無料）<br />
・<a href="https://www.codexa.net/pandas/">Pandas 入門</a>（無料）<br />
・<a href="https://www.codexa.net/matplotlib/">Matplotlib 入門</a>（無料）<br />
・<a href="https://www.codexa.net/linear-basics/">線形代数</a>（無料）<br />
・<a href="https://www.codexa.net/statistics-for-machine-learning-first/">統計基礎</a>（無料）</p>
<p>また、すでに機械学習の基礎知識がある方に向けて、機械学習の様々な手法を詳しく解説したチュートリアルも公開しています。</p>
<p>・<a href="https://www.codexa.net/tutorial-linear-regression-for-beginner/">実践 線形回帰</a><br />
・<a href="https://www.codexa.net/tutorial-logistic-regression-for-beginner/">実践 ロジスティック回帰</a><br />
・<a href="https://www.codexa.net/decision-tree-random-forest/">決定木とランダムフォレスト</a><br />
・<a href="https://www.codexa.net/support-vector-machine-tutorial/">サポートベクターマシン</a><br />
・<a href="https://www.codexa.net/naive-bayes-tutorial/">ナイーブベイズ（単純ベイズ分類器）</a><br />
・<a href="https://www.codexa.net/xgboost-tutorial/">XGBoost</a><br />
・<a href="https://www.codexa.net/keras-cnn-python/">はじめての画像認識</a></p>
<p>皆様の受講をお待ちしております。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.codexa.net/missing_value_python/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ダミー変数（One-Hotエンコーディング）とは？実装コードを交えて徹底解説</title>
		<link>https://www.codexa.net/get_dummies/</link>
					<comments>https://www.codexa.net/get_dummies/#respond</comments>
		
		<dc:creator><![CDATA[codexaチーム]]></dc:creator>
		<pubDate>Thu, 15 Apr 2021 09:00:29 +0000</pubDate>
				<category><![CDATA[機械学習]]></category>
		<guid isPermaLink="false">https://www.codexa.net/?p=2848</guid>

					<description><![CDATA[ダミー変数（別名：One-Hotエンコーディング）とはカテゴリカル（質的）データを0又は1で表現した変数を指します。本稿では機械学習でもよく用いられるダミー変数について実装可能なサンプルコードを踏まえて詳しく解説していきます。]]></description>
										<content:encoded><![CDATA[<p>「データ」という言葉は非常によく使われる言葉です。AIや機械学習も、データを大量に収集し、それらを学習することで予測を立てています。しかし、データを大量に収集できれば、残りの作業をすべてAIが行ってくれるわけではありません。AIがより精度の高い予測を出すためには、これらのデータをどのように処理するかが重要になってきます。</p>
<p>データには大きく分けて量的データとカテゴリカル（質的）データの2種類が存在します。量的データは身長や時刻などの数値として意味があるものを指します。逆にカテゴリカルデータは性別や順位などの分類や区別を行うためのデータです。収集したデータの特徴を捉えることはデータ分析にとって重要な技術です。特に、カテゴリカルデータに関しては、そのままの形ではデータ分析で扱いにくい場合があります。そのため、前処理を行い、扱いやすいデータに直してあげる必要があります。</p>
<p>本稿ではカテゴリカルデータに対する前処理に注目し、機械学習でもよく用いられるダミー変数について解説していきます。また、後半では実際のデータセットを用いたOne-Hotエンコーディングの実装方法も解説します。本稿が機械学習を勉強し始めた方、kaggleなどのデータ分析コンペティションに挑戦したいと考えている方のお役に立つことができれば幸いです。</p>
<h3>ダミー変数の概要</h3>
<p>この節では、ダミー変数の概要について説明していきます。まずはダミー変数について正確に理解し、その後、実際のデータセットを使用した実装方法に触れていきましょう。</p>
<h4>ダミー変数の定義</h4>
<p>ダミー変数とは、カテゴリカル（質的）データを0又は1で表現した変数のことです。</p>
<p>例えば、「性別」の列に「男性」と「女性」というカテゴリカルデータが存在したとします。このデータは数値ではないので、実際にデータ分析を行うには少し扱いづらいです。この時、「男性であるかどうか？」、「女性であるかどうか？」をそれぞれ1と0で表します。このように数値として扱えないデータを0と1に数値化する際に用いられる変数がダミー変数です。</p>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/03/men_women.png" alt="" /></p>
<p>ダミー変数の定義を「カテゴリカルデータを0と1で表現した変数のこと」と記載しましたが、実施にはカテゴリカルデータにも分類が存在します。ダミー変数についての詳細に触れる前に、カテゴリカルデータの定義についても確認しておきたいと思います。カテゴリカルデータには順序尺度と名義尺度が存在します。この2つの尺度の違いはダミー変数にとって重要な内容なため、しっかりと理解しておきましょう。</p>
<h5>順序尺度（Ordinal scale）</h5>
<p>順序尺度は数値自体は意味を持たないが、数値の順序に対して意味があるものです。例えば、地震の震度などがこれに相当します。震度は数字が大きくなるにつれて揺れも大きくなりますが、震度3が震度1の3倍強いわけではありません。そのため、震度にを示す数値自体や数値の間隔には意味を持たず、数値の大小関係のみが意味を持つことになります。</p>
<h5>名義尺度（Nominal scale）</h5>
<p>名義尺度は数値自体に意味がないのに加えて、順序に対しても意味がないものです。例えば、性別や血液型などがこれに相当します。血液型は数値ではないので、「A型」「B型」「O型」「AB型」をそれぞれ「1（A型）」「2（B型）」「3（O型）」「4（AB型）」の数値として置き換えて考えてみます。しかし、これらの数値に意味はありません。「4（AB型）」が「1（A型）」の4倍の何かを持っているわけではありません。同様に、順序も存在していません。あくまで分類のための名前として、それぞれのデータが独立して成り立っています。</p>
<p>この2つの尺度について理解することができたでしょうか？用語を聞くと難しく感じるかもしれませんが、ダミー変数自体は0と1のシンプルな変数です。この2つの尺度を理解した上で実際にダミー変数が用いられる場面を確認してみましょう。それほど難しくは感じないはずです。</p>
<h4>ダミー変数が用いられる場面</h4>
<p>カテゴリカルデータに存在する2つの尺度を確認したところで改めてダミー変数の話に戻ります。実際のダミー変数は、先に述べた2つの尺度の内、名義尺度の方に適用されることが一般的です。そのため、ダミー変数を更に詳細に定義すると「名義尺度のカテゴリカルデータを0または1で表現した変数」ということになります。何度も述べますようにダミー変数は0と1のみの値しか使用しません。順序を表現することは0と1の値だけでは困難なため、ダミー変数が用いられることは基本的にはありません。</p>
<p>実際に順序尺度と名義尺度のカテゴリカルデータをダミー変数の定義に基づいて表現してみましょう。</p>
<h5>順序尺度をダミー変数で変換した場合</h5>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/03/震度.png" alt="" /></p>
<p>順序尺度をダミー変数で変換した場合を見てみましょう。「震度」の列には「震度1」「震度２」「震度３」を表した変数が存在しています。それをダミー変数を用いて変換すると3列の表になります。この時の2つの表の違いに着目してみましょう。</p>
<p>震度の列には大小関係が存在しています。揺れは「震度2」が「震度1」よりも大きく、「震度3」は「震度2」よりも大きいことは既知です。変換前の表は震度の変数が順番に並んでいるため、大小関係を認識することができます。対して、変換後の表はそれぞれの震度を3列に分けて表現されています。つまり、それぞれの震度が独立して存在していることになります。このような変換を行なってしまうと、震度自体を数値に変換することはできていますが、震度に対する大小関係が認識できなくなってしまっています。</p>
<h5>名義尺度をダミー変数で変換した場合</h5>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/03/血液型.png" alt="" /></p>
<p>名義尺度をダミー変数で変換した場合を見てみましょう。「血液型」の列には「A型」「B型」「O型」「AB型」を表した変数が存在しています。それをダミー変数を用いて変換すると4列の表になります。この時の2つの表の違いに着目してみましょう。</p>
<p>血液型には元から大小関係は存在していません。変換前の表に注目しても血液型が4種類存在しているというだけで、それぞれの血液型に差はなく、平等に扱うことができます。ダミー変数を用いた変換後の表も見てみましょう。変換後の表ではそれぞれの血液型が4つの列で表現されています。これも、それぞれの血液型が独立して存在していますが、血液型自体に大小関係は存在していないため、問題ありません。</p>
<p>順序尺度と名義尺度をダミー変数で変換した時の違いについて触れてきました。ここまでの説明で、ダミー変数を名義尺度のカテゴリカルデータに適用する理由はご理解いただけたかと思います。次はダミー変数を用いたOne-Hotエンコーディングと呼ばれる前処理の手法について触れていきます。</p>
<h4>One-Hotエンコーディングとは</h4>
<p>急にOne-Hotエンコーディングという名前が登場したため、戸惑われている方もいらっしゃるかもしれませんが、基本的にはここまでに説明した「ダミー変数を用いた変換」と同義です。機械学習の分野ではダミー変数を用いた前処理をOne-Hotエンコーディングと呼びます。先に述べた血液型のように、数値として扱えない型などで表されたデータを数値に変換する際に使用されます。</p>
<p><img src="https://www.codexa.net/wp-content/uploads/2021/03/One-Hotエンコーディング.png" alt="" /></p>
<p>One-Hotエンコーディング自体はシンプルな手法ですが、同時に注意点も存在します。上記の図を見ても分かるように変換前の表は4×1の大きさなのに対し、変換後の表は4×4の大きさになっています。つまり元の表に存在するユニークな値の数だけ、変換後に列が作られる事になります。今回は4種類程度でしたが、ユニークな値の数が膨大になればその分の列を作成しなければなりません。つまりメモリの消費量が大きくなります。大規模データセットに対してOne-Hotエンコーディングを行う場合は、そういった点も考慮する必要があります。</p>
<p>実際のデータ分析では様々な前処理を駆使してデータを扱いやすい形に変換させていきます。カテゴリカルデータに0から順番に番号を振るLabelエンコーディングや、カテゴリカルデータの出現回数をカウントするCountエンコーディングなどが存在します。機械学習を勉強される方にとっては頻出用語ですので、様々なエンコーディング手法を調べてみてください。今回のようなデータ分析コンペティションでよく使用される前処理が記載された本なども発売されています。業務よりもコンペの方に興味がある方は読んでみてください。（参考：<a href="https://www.amazon.co.jp/Kaggle%E3%81%A7%E5%8B%9D%E3%81%A4%E3%83%87%E3%83%BC%E3%82%BF%E5%88%86%E6%9E%90%E3%81%AE%E6%8A%80%E8%A1%93-%E9%96%80%E8%84%87-%E5%A4%A7%E8%BC%94-ebook/dp/B07YTDBC3Z">Kaggleで勝つデータ分析の技術</a>）</p>
<p>ここまでは理論を中心に説明してきましたが、この後は実際にPythonを用いてOne-Hotエンコーディングを実装していきたいと思います。理論と実装の両方を抑え、ダミー変数とOne-Hotエンコーディングをしっかりと理解しましょう。</p>
<h3>One-Hotエンコーディングの実装</h3>
<p>本節では、One-Hotエンコーディングを機械学習ライブラリでよく用いられるpandasとscikit-learnを用いた2通りの手法で実装していきます。本稿では、Google Colabを用いて実装していきます。本稿は2021年3月8日時点でコードの実行確認を行いましたので、Google Colabのデフォルトのバージョンが変更されない限り、ライブラリをそのままインポートすれば同じように実装可能です。是非、ご自身でも実装してみてください。（参考：<a href="https://www.codexa.net/machine-learning-python-library/">Python 機械学習ライブラリを23個一挙紹介！データ処理から深層学習まで完全網羅</a>・<a href="https://www.codexa.net/how-to-use-google-colaboratory/">Google Colabの知っておくべき使い方</a>）</p>
<p>それではOne-Hotエンコーディングの実装を行なっていきます。はじめに、必要なライブラリを読み込みます。</p><pre class="crayon-plain-tag">#[IN]:

#ライブラリの読み込み
import pandas as pd
import numpy as np
import seaborn as sns
from sklearn.preprocessing import OneHotEncoder</pre><p>
データセットにはkaggleのチュートリアルで最も有名なTitanicのデータセットを利用します。Titanicのデータセットはseaborn上に用意されているので、今回はそちらを利用します。kaggle上で提供されるTitanicとseaborn上のTitanicのデータセットは若干の違いがあるため、注意してください。</p>
<p>Titanicコンペティションの解説はAIマガジンの<a href="https://www.codexa.net/kaggle-titanic-beginner/">【Kaggle初心者入門編】タイタニック号で生き残るのは誰？</a>でも紹介しています。今回は投稿は行わないため、投稿まで行いたい方はこちらを参考に挑戦してみて下さい。（参考：<a href="https://www.kaggle.com/c/titanic">Titanic &#8211; Machine Learning from Disaster</a>）</p><pre class="crayon-plain-tag">#[IN]:

#titanicデータセットを読み込んで、一部を表示
df = sns.load_dataset('titanic')
df.head()</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/03/スクリーンショット-2021-03-09-14.29.35.png" alt="" /></p>
<p>データセットの最初5行が表示されました。今回はOne-Hotエンコーディングの実装が目的なため、データセットの中から名義尺度であるものを抜き出します。変換後の結果が分かりやすいようにsex（性別）とembark_town（出航地名）を使用してみましょう。</p><pre class="crayon-plain-tag">#[IN]:

#sex（性別）とembark_town（出航地名）を抜き出して表示
df = df[["sex","embark_town"]]
df</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/03/スクリーンショット-2021-03-09-14.32.46.png" alt="" /></p>
<p>sex（性別）とembark_town（出航地名）を抜き出すことができました。念のため、欠損値を確認して、存在していれば削除してしましょう。</p><pre class="crayon-plain-tag">#[IN]:

#欠損値の有無を確認
df.isnull().sum()</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

sex 0
embark_town 2
dtype: int64</pre><p>
</p><pre class="crayon-plain-tag">#[IN]:

#欠損値を含む行を削除して、表示
df = df.dropna()
df</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/03/スクリーンショット-2021-03-09-14.38.08.png" alt="" /></p>
<p>embarkedに欠損値が2つあったため、対象の行を削除しました。次に、それぞれの列に何種類のカテゴリカルなデータが存在するのかを調べたいと思います。そのために、それぞれの列からユニークな値を表示します。</p><pre class="crayon-plain-tag">#[IN]:

#「sex」列のユニークな値を表示
print(df["sex"].unique())
#「embark_town」のユニークな値を表示
print(df["embark_town"].unique())</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

['male' 'female']
['Southampton' 'Cherbourg' 'Queenstown']</pre><p>
ユニークな値を調べたところ「sex」の列には「male」と「female」の2種類、「embark_town」の列には「Southampton」と「Cherbourg」と「Queenstown」の3種類の値が存在することが分かりました。これで、One-Hotエンコーディングを行うための準備が整いました。まずはpandasのget_dummies()を用いてOne-Hotエンコーディングを実装していきます。pandasのget_dummies()をデフォルトで使用すると、変換後の列名が「変換前の列名_変換前の変数名」になります。引数を渡すことによって、欠損値をダミー変数で扱ったり、列名を変更したりすることができます。詳しく知りたい方はpandasの公式ドキュメントを参考にしてみてください。（参考：<a href="https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.get_dummies.html">pandas.get_dummies（pandas公式）</a>・<a href="https://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B%EF%BC%81pandas%E3%83%87%E3%83%BC%E3%82%BF%E5%89%8D%E5%87%A6%E7%90%86%E5%85%A5%E9%96%80-%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%83%BB%E3%83%87%E3%83%BC%E3%82%BF%E3%82%B5%E3%82%A4%E3%82%A8%E3%83%B3%E3%82%B9%E3%81%A7%E5%BD%B9%E7%AB%8B%E3%81%A4%E5%89%8D%E5%87%A6%E7%90%86%E6%89%8B%E6%B3%95-%E6%A0%AA%E5%BC%8F%E4%BC%9A%E7%A4%BE%E3%83%AD%E3%83%B3%E3%83%90%E3%83%BC%E3%83%88-ebook/dp/B084MD5DGG">現場で使える！pandasデータ前処理入門</a>）</p><pre class="crayon-plain-tag">#[IN]:

#pandasのget_dummiesを用いてOne-Hotエンコーディングを実装
pd_df = pd.get_dummies(df)
pd_df</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/03/スクリーンショット-2021-03-09-14.42.44.png" alt="" /></p>
<p>ダミー変数によってOne-Hotエンコーディングが行われたことが確認できました。前半で説明したように、「sex」の列が「sex_female」と「sex_male」に、「embark_town」の列が「embark_town_Cherbourg」と「embark_town_Queenstown」、「embark_town_Southampton」として新しい列に変換されています。確認すると、変換前の列に存在した変数が新しい列では1と0で表現されています。</p>
<p>続いてscikit-learnのOneHotEncoder()を用いてOne-Hotエンコーディングを行なっていきます。OneHotEncoder()では引数をsparse=Falseに設定し、返り値を配列にして見やすくしています。sparse=Trueにした場合は疎行列が返り値に設定されます。その次の行の処理では、dtypes=intにすることで整数値を返しています。デフォルトではfloatになっています。OneHotEncoder()には他にも引数を設定できるので、興味がある方はscikit-learnの公式ドキュメントを参考にしてみてください。（参考：<a href="https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html">sklearn.preprocessing.OneHotEncoder（scikit-learn公式</a>））</p><pre class="crayon-plain-tag">#[IN]:

#scikit-learnのOneHotEncoder()を用いてOne-Hotエンコーディングを実装
oe = OneHotEncoder(sparse = False,dtype = int)
sl_np = oe.fit_transform(df)
sl_np</pre><p>
</p><pre class="crayon-plain-tag">#[OUT]:

array([[0, 1, 0, 0, 1],
[1, 0, 1, 0, 0],
[1, 0, 0, 0, 1],
...,
[1, 0, 0, 0, 1],
[0, 1, 1, 0, 0],
[0, 1, 0, 1, 0]])</pre><p>
返り値がnumpyのarrayになってます。scikit-learnのOneHotEncoder()ではDataFrameを返すことができません。そのため、pd.Dataframe()を用いてデータフレームに直します。分かりやすいようにpandasのget_dummies()を使用した場合と同様の列名を指定します。</p><pre class="crayon-plain-tag">#[IN]:

#numpy.arrayからobject型に直す
sl_df = pd.DataFrame(data = sl_np,columns = ["sex_female","sex_male","embark_town_Cherbourg","embark_town_Queenstown","embark_town_Southampton"])
sl_df</pre><p>
<img src="https://www.codexa.net/wp-content/uploads/2021/03/スクリーンショット-2021-03-09-14.47.19.png" alt="" /></p>
<p>ダミー変数によってOne-Hotエンコーディングが行われたことが確認できました。今回は欠損地を予め削除したため、問題は起こりませんでしたが、scikit-learnのOneHotEncoder()はデータ内の欠損値に対応出来ません。そのため、今回のように予め欠損値を削除しておくか、平均値などで埋めておく必要があります。pandasのget_dummies()ではデフォルトでは欠損値をすべての列で0として扱うため、エラーの発生という面では起こりにくくなります。</p>
<p>ここまで、pandasのgetdummies()とscikit-learnのOneHotEncoder()の2つの実装を紹介してきました。一見するとget_dummies()の方が扱いやすそうに感じますが、データによっては注意が必要です。pandasのDataFrameはnumpyの配列と比較してメモリの消費量が多い傾向があるため、機械学習などで使用される大規模データセットを用いた際にはメモリエラーとなる場合があります。大規模データセットを使用する際にはOneHotEncoder()を使用したり、sparse=Trueに設定して疎行列を出力するなどの工夫が必要です。</p>
<p>ダミー変数を扱う際には、多重共線性と呼ばれる問題が発生する場合があります。多重共線性を理解するためには数学的な知識が必要になってくるため、詳細について本稿では説明しません。しかし、統計学と機械学習の両分野において重要な用語ですので、興味がある方は調べてみるとをオススメします。</p>
<h3>まとめ</h3>
<p>いかがでしたでしょうか。本稿を通して、ダミー変数及び、One-Hotエンコーディングについて少しでも理解を深めることができたら幸いです。データに対する前処理は機械学習の分野では避けては通れない道です。ダミー変数だけでなく、多くの前処理を勉強し実践してみてください。正確で効果的な前処理を実装できるようになれば、機械学習エンジニアに一歩近づくはずです。</p>
<p>codexaでは機械学習の勉強を簡単に始められるコースをご用意しています。前処理についても基礎からしっかりと抑えることができます。是非、ご受講をお待ちしております。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.codexa.net/get_dummies/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>【全22種 AI資格掲載中】国内外のAI資格を徹底調査しました</title>
		<link>https://www.codexa.net/ai_certification/</link>
					<comments>https://www.codexa.net/ai_certification/#respond</comments>
		
		<dc:creator><![CDATA[codexaチーム]]></dc:creator>
		<pubDate>Tue, 16 Mar 2021 02:00:19 +0000</pubDate>
				<category><![CDATA[機械学習]]></category>
		<guid isPermaLink="false">https://www.codexa.net/?p=2830</guid>

					<description><![CDATA[国内外のAI資格 全22種を徹底調査しました！AI資格は機械学習の基礎的な領域を問うものから、コーディングのような実装的な領域を問うものまで幅広く存在しています。本稿では「AI実装関連資格」「クラウド系AI資格」「統計系AI資格」「その他AI資格」として、各AI資格の概要を徹底調査しました。]]></description>
										<content:encoded><![CDATA[<p>昨今、「AI」と言う言葉を聞かない日はないほどにAIブームが加速しています。それによりAIや機械学習について勉強したいと思う方が増えたことは間違いありません。最近では「AI資格」と呼ばれる資格試験も誕生し始め、多くの方が受験されています。例えば、一般社団法人日本ディープラーニング協会が主宰するG検定では2020年11月時点で累計受験者数が47375人に上っています。（参考：<a href="https://www.codexa.net/jdla-generalist-test/">G検定とは？</a>）</p>
<div class="su-note"  style="border-color:#dadada;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;"><div class="su-note-inner su-u-clearfix su-u-trim" style="background-color:#f4f4f4;border-color:#ffffff;color:#333333;border-radius:3px;-moz-border-radius:3px;-webkit-border-radius:3px;"><strong>AI資格とは？<br />
</strong>機械学習の実装、理論や歴史、また機械学習の実装に深く関係する統計学やプログラミング言語（ライブラリを含む）の基礎的な知識などを客観的に測る目的とした資格を本稿では「AI資格」とします。また、「機械学習資格」などとも呼ばれます。</div></div>
<p>本稿を読まれている方の中にも「AI資格」を取得したいと考えている方がいらっしゃるのではないでしょうか？</p>
<p>AIの関連領域は多岐に渡ります。AI資格も機械学習の基礎的な領域を問うものから、コーディングのような実装的な領域を問うものまで幅広く存在しています。そのため、AI資格を取得したいと考えた際に、どの資格を取得すべきか分からないという状況に陥りやすくなっています。</p>
<p>本稿では国内外のAI資格を徹底調査しました。本稿を読んだみなさまがAI資格を受験される際に、判断材料の一つにしていただけたら幸いです。</p>
<h2>日本語で受験可能なAI資格一覧</h2>
<p>現在、世界中でAIに関連した資格や講座が存在しています。その中でもまずは国内のAI資格に目を向けてみましょう。AIを勉強する上ではライブラリの公式ドキュメントの読解など、英語の能力が求められる場面が多々存在します。しかし、いきなり海外資格に挑むことは、AIをこれから勉強する人や英語が苦手な人にとっては非常に高いハードルです。そのため、まずは日本語で受験可能なAI資格を探して見ることをオススメします。</p>
<p>国内でも一般社団法人日本ディープラーニング協会（JDLA）をはじめ、多くの団体がAI資格を主宰しています。本稿ではそれらを「AI実装関連資格」「クラウド系AI資格」「統計系AI資格」「その他AI資格」として紹介していきます。基礎、実装、統計、画像、様々な分野でAI資格は存在するため、ご自身が身に付けたい領域と比べながら１つ１つ見ていきましょう。</p>
<h3>AI実装関連の資格</h3>
<p>まずはAI実装関連の資格です。AI資格の中では最も機械学習の根幹に近い分野を対象にした試験です。他の分野との組み合わせが少なく、試験内容も理解しやすいです。初学者の方や、機械学習を使用する目的がまだ決まってない方でも受験しやすい資格です。</p>
<h4>G検定（ジェネラリスト検定）</h4>
<div style="width: 160px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2021/08/icon_general2.png" alt="" width="150" height="150" /><p class="wp-caption-text">（引用：<a href="https://www.jdla.org/">一般社団法人日本ディープラーニング協会</a>）</p></div>
<p>G検定は2017年にスタートした資格で、一般社団法人日本ディープラーニング協会が主宰しています。2020年11月時点での累計受験者数は47375人と非常に多くの方が受験されており、国内のAI資格の中では認知度が非常に高い資格となっています。G検定はディープラーニングを事業活用する人材（ジェネラリスト）を認定するための検定として行われていて、後述するE資格とは違い受験資格に制限がありません。また、コーディングの問題がないという特徴があります。そのため、機械学習に対する知識がない方や、初学者の方でも受けやすい試験です。</p>
<p>公式にG検定の例題や推薦図書などが紹介されており、AI資格の中でも勉強のための情報が多い試験です。勉強法や体験記をあげている方も多いので、それらを参考にしながら勉強することができます。毎回の試験で合格率は60%近くに達します。そのため、しっかりと試験対策を行った方にとっては難易度はそこまで高くありません。（参考：<a href="https://www.jdla.org/wp-content/uploads/2020/11/JDLA%E8%A9%A6%E9%A8%93%E5%AE%9F%E6%96%BD%E3%83%AC%E3%83%9B%E3%82%9A%E3%83%BC%E3%83%88_202011%E7%89%88v2.pdf">JDLA試験実施レポート_202011版v2.pdf</a>・<a href="https://www.jdla.org/certificate/general/issues/">G検定の例題</a>・<a href="https://www.jdla.org/recommendedbook/#recommendedbookAnchor_No03">推薦図書</a>）</p>
<p>AIマガジンでも過去に<a href="https://www.codexa.net/jdla-generalist-test/">今注目のAI関連資格「G検定」とは？合格者が勉強法を徹底解説！</a><br />
で、実際にG検定の合格者が試験内容から勉強方法まで徹底解説しています。こちらの記事も参考にしながら、G検定への対策を行ってみてください。</p>
<table align="center">
<tbody>
<tr>
<td>主宰</td>
<td>一般社団法人日本ディープラーニング協会</td>
</tr>
<tr>
<td>公式URL</td>
<td><a href="https://www.jdla.org/certificate/general/">https://www.jdla.org/certificate/general/</a></td>
</tr>
<tr>
<td>受験資格</td>
<td>誰でも受験可能</td>
</tr>
<tr>
<td>試験日程</td>
<td>年３回程度</td>
</tr>
<tr>
<td>試験形式</td>
<td>オンライン</td>
</tr>
<tr>
<td>試験時間</td>
<td>120分</td>
</tr>
<tr>
<td>受験費用</td>
<td>一般：12000円/学生：5000円</td>
</tr>
</tbody>
</table>
<h4>E資格</h4>
<div style="width: 160px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2021/08/icon_engineer2.png" alt="" width="150" height="150" /><p class="wp-caption-text">（引用：<a href="https://www.jdla.org/">一般社団法人日本ディープラーニング協会</a>）</p></div>
<p>E資格もG検定と同様に一般社団法人日本ディープラーニング協会が主宰しています。G検定と同様に国内のAI資格の中では認知度が高い資格です。E資格はディープラーニングを実装する人材（エンジニア）を育成するための資格として位置付けられています。そのため、エンジニアとして機械学習に関する能力を証明したい方向けの試験です。</p>
<p>試験では機械学習の基礎からディープラーニングの実装までの能力を問われます。毎回の試験の合格率は65%近くになります。試験自体の難易度は高くないように感じますが、E資格の受験者は全員JDLA認定プログラムを修了しています。これからE資格に挑戦しようとしている方もJDLA認定プログラムを修了する必要があります。</p>
<p>受験資格が存在するという点が、E資格とG検定の最も大きさな違いです。E資格ではJDLAの認定プログラムの修了が必須になります。JDLA認定プログラムは認定プログラム公式一覧よりご確認いただけます。認定プログラムは数多く存在し、ご自身にあったプログラムを選択する必要があります。期間が長く網羅的に勉強するものから、短期間で集中的に勉強するものまで様々なので、よく検討した上で選びましょう。（参考：<a href="https://www.jdla.org/certificate/engineer/#certificate_No04">認定プログラム公式一覧</a>）</p>
<table align="center">
<tbody>
<tr>
<td>主宰</td>
<td>一般社団法人日本ディープラーニング協会</td>
</tr>
<tr>
<td>公式URL</td>
<td><a href="https://www.jdla.org/certificate/engineer/">https://www.jdla.org/certificate/engineer/</a></td>
</tr>
<tr>
<td>受験資格</td>
<td>JDLA認定プログラムを試験日の過去２年以内に修了していること</td>
</tr>
<tr>
<td>試験日程</td>
<td>年2回程度</td>
</tr>
<tr>
<td>試験形式</td>
<td>会場受験</td>
</tr>
<tr>
<td>試験時間</td>
<td>120分</td>
</tr>
<tr>
<td>受験費用</td>
<td>一般：33000円/学生：22000円/会員：27500円</td>
</tr>
</tbody>
</table>
<h4>AI実装検定</h4>
<div style="width: 160px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2021/08/Ai_IMP_exam_logo-116x150-1.jpeg" alt="" width="150" height="150" /><p class="wp-caption-text">（引用：<a href="https://kentei.ai/">AI実装検定</a>）</p></div>
<p>AI実装検定は「AIを100万人が学ぶこと」を意義に設立されたStudy-AI株式会社が主宰している検定です。AIの実装検定の特徴は以下の３点になります。<br />
1. ディープラーニングの実装に必要な数学の知識<br />
2. ディープラーニングの実装に必要なプログラミングの知識<br />
3. ディープラーニングの実装について基礎理論の理解</p>
<p>レベル別にB級、A級、S級と3つの級が存在するため、ご自身のレベルにあった検定を受験することが可能です。AI実装検定は中学校までの義務教育を受けていれば誰でも挑戦することができると記載されています。そのため、G検定と同様に初学者の方も受験しやすい試験です。</p>
<table align="center">
<tbody>
<tr>
<td>主宰</td>
<td>Study-AI株式会社</td>
</tr>
<tr>
<td>公式URL</td>
<td><a href="https://kentei.ai/">https://kentei.ai/</a></td>
</tr>
</tbody>
</table>
<h5>AI実装検定 B級</h5>
<div style="width: 160px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2021/08/LevelB_logo-120x150-1.jpeg" alt="" width="150" height="150" /><p class="wp-caption-text">（引用：<a href="https://kentei.ai/b">AI実装検定®B級</a>）</p></div>
<p>B級はAIの知識が全くない入門者でも気軽に挑戦できる位置付けになっています。公式の試験レベルによると、G検定の範囲と比べても10倍範囲が狭く、想定勉強時間も5時間に設定されています。試験時間も40分、受験費用も2200円と非常にお手頃なため、機械学習にまず触れてみたい方向けの試験です。</p>
<p>Youtubeに公式教材がアップされているため興味がある方は確認してみてください。（参考：<a href="https://www.youtube.com/playlist?reload=9&amp;list=PLWlPLLc6IHrjRV-3AmgEBr2gG4lOy3m2e">公式教材</a>）</p>
<table align="center">
<tbody>
<tr>
<td>受験資格</td>
<td>誰でも受験可</td>
</tr>
<tr>
<td>試験日程</td>
<td>年1回程度</td>
</tr>
<tr>
<td>試験形式</td>
<td>オンライン</td>
</tr>
<tr>
<td>試験時間</td>
<td>40分</td>
</tr>
<tr>
<td>受験費用</td>
<td>2200円　2022年より一般：9900円/学生：5500円</td>
</tr>
</tbody>
</table>
<h5>AI実装検定 A級</h5>
<div style="width: 160px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2021/08/LevelA_logo-118x150-1.jpeg" alt="" width="150" height="150" /><p class="wp-caption-text">（引用：<a href="https://kentei.ai/a">AI実装検定®A級</a>）</p></div>
<p>A級はE資格の認定プログラムに挑戦できるレベルに位置づけれられています。試験自体もB級よりもディープラーニングの基礎知識やプログラミングの実装能力が要求されます。それでも、E資格と比較すると試験時間も短く、受験費用も抑えられています。E資格の認定プログラムを受ける前にディープラーニングの知識をある程度身に付けておきたい方向けの試験です。</p>
<p>A級の公式教材として「超入門AI講座（有料）」が紹介されています。こちらも是非確認してみてください。（参考：<a href="https://kentei.ai/course/a/">超入門AI講座（有料）</a>）</p>
<table align="center">
<tbody>
<tr>
<td>受験資格</td>
<td>誰でも受験可</td>
</tr>
<tr>
<td>試験日程</td>
<td>年2回程度</td>
</tr>
<tr>
<td>試験形式</td>
<td>オンライン</td>
</tr>
<tr>
<td>試験時間</td>
<td>60分</td>
</tr>
<tr>
<td>受験費用</td>
<td>3850円　2022年より一般：14850円/学生：8250円</td>
</tr>
</tbody>
</table>
<h5>AI実装検定 S級</h5>
<div style="width: 160px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2021/08/LevelS_logo-119x150-1.jpeg" alt="" width="150" height="150" /><p class="wp-caption-text">（引用：<a href="https://kentei.ai/s">AI実装検定®S級</a>）</p></div>
<p>S級はAIの実装力に加えて画像処理をメインとした応用的な分野への実装能力が求められています。公式の試験レベルによると、E資格に比べて試験の範囲は狭く、実装の難易度は同程度のものに設定されています。そのため、E資格の認定プログラムを受講しながら、並行して取得を目材したり、E資格の受験前に実力を図りたい方向けの試験です。</p>
<p>S級の公式教材として「画像処理100本ノック」や「ディープラーニング∞本ノック」が紹介されています。これらの教材はPythonの実装能力向上にも非常に役に立ちます。こちらも是非挑戦してみてください。（参考：<a href="https://github.com/yoyoyo-yo/Gasyori100knock">画像処理100本ノック</a>・<a href="https://github.com/yoyoyo-yo/DeepLearningMugenKnock">ディープラーニング∞本ノック</a>）</p>
<table align="center">
<tbody>
<tr>
<td>受験資格</td>
<td>誰でも受験可</td>
</tr>
<tr>
<td>試験日程</td>
<td>年1回程度</td>
</tr>
<tr>
<td>試験形式</td>
<td>オンライン</td>
</tr>
<tr>
<td>試験時間</td>
<td>60分</td>
</tr>
<tr>
<td>受験費用</td>
<td>5500円　2022年より33500円</td>
</tr>
</tbody>
</table>
<h3>クラウド系AI資格</h3>
<p>クラウド（クラウド・コンピューティング）とは、利用者側がソフトウェアやサーバなどのネットワークを持たずとも、インターネットを通じて利用できるサービス全体のことを指します。また、利用できるサービスのことをクラウドサービスと呼びます。クラウドについて深く知りたい方は下記リンクを参考に調べてみてください。（参考：<a href="https://aws.amazon.com/jp/cloud/">クラウド（クラウドサービス）とは？（AWS）</a>）</p>
<p>クラウド系AI資格では主に、Amazon Web Services（AWS）、Microsoft Azure（Azure）、Google Cloud Platform（GCP）などのクラウドサービスに関連した資格を取り上げました。ここ数年クラウドの市場は右肩上がりで成長しています。特に、大企業の約87%がクラウド環境を利用したシステム開発を推進しています。（参考：<a href="https://www.m2ri.jp/release/detail.html?id=434">国内クラウド市場は2024年に5兆円超え（株式会社MM総研）</a>）</p>
<p>これらは、AIや機械学習の分野でも例外ではありません。今後、クラウド環境を利用した機械学習関連の開発は増えていくと予想されます。そのため、クラウド系のAI資格は現状のニーズに非常にマッチした資格と言えます。</p>
<h4>AWS 認定機械学習専門知識(Machine Learning Specialty)</h4>
<div style="width: 210px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2021/08/AWS-Certified_Machine-Learning_Specialty_512x512.6ac490d15fe033a3d67ca544ecd0bcbcb10d391a.png" alt="" width="200" height="200" /><p class="wp-caption-text">（引用：<a href="https://aws.amazon.com/jp/certification/">AWS認定</a> ）</p></div>
<p>AWS 認定機械学習専門知識は国内のクラウドサービスの利用率で1位のAmazon Web Service（以下AWS）による認定資格です。AWSはこの試験によって検証される能力は下記のように設定されています。</p>
<ul>
<li>与えられたビジネスの問題に対し、適切な機械学習のアプローチを選び、その理由を説明できる</li>
<li>機械学習を用いた解決策の実装に適したAWS のサービスを選択する</li>
<li>拡張性、コスト効率、信頼性、安全性に優れた機械学習の解決法を設計し、実装する</li>
</ul>
<p>また、推奨とされる知識と経験としては下記のように設定されています。</p>
<ul>
<li>AWSクラウドでの機械学習/深層学習ワークロードの開発、設計、実行における、1～2 年の経験</li>
<li>基本的な機械学習アルゴリズムの基となる考えを表現する能力</li>
<li>基本的なハイパーパラメータ最適化の実践経験</li>
<li>機械学習および深層学習フレームワークの使用経験</li>
<li>モデルトレーニングの最も効率の良い手法を実行する能力</li>
<li>配置と運用の最も効率の良い手法を実行する能力</li>
</ul>
<p>これらの記載をみても分かるように、機械学習の知識だけでなく、AWSや実務の能力も要求されます。そのためAWSを実際に業務で使用されている方、機械学習とAWSを同時に勉強したい方向けの試験です。</p>
<p>試験は100~1000点の範囲で評価され合格ラインは750点です。試験ガイドやサンプル問題、模擬試験は提供されています。それらを活用しながら合格を目指しましょう。（参考：<a href="https://d1.awsstatic.com/ja_JP/training-and-certification/docs-ml/AWS-Certified-Machine-Learning-Specialty_Exam-Guide.pdf">試験ガイド</a>・<a href="https://d1.awsstatic.com/ja_JP/training-and-certification/docs-ml/AWS-Certified-Machine-Learning-Specialty_Sample-Questions.pdf">サンプル問題</a>）</p>
<table align="center">
<tbody>
<tr>
<td>主宰</td>
<td>Amazon Web Service</td>
</tr>
<tr>
<td>公式URL</td>
<td><a href="https://aws.amazon.com/jp/certification/certified-machine-learning-specialty/">https://aws.amazon.com/jp/certification/certified-machine-learning-specialty/</a></td>
</tr>
<tr>
<td>受験資格</td>
<td>誰でも受験可能</td>
</tr>
<tr>
<td>試験日程</td>
<td>随時</td>
</tr>
<tr>
<td>試験形式</td>
<td>テストセンター又はオンライン</td>
</tr>
<tr>
<td>試験時間</td>
<td>180分</td>
</tr>
<tr>
<td>受験費用</td>
<td>30000円（模擬試験：4000円）</td>
</tr>
</tbody>
</table>
<h4>Microsoft Certified: Azure AI Fundamentals</h4>
<h5>試験 AI-900: Microsoft Azure AI Fundamentals</h5>
<div style="width: 210px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2021/08/スクリーンショット-2021-08-12-12.58.56.png" alt="" width="200" height="200" /><p class="wp-caption-text">（引用：<a href="https://docs.microsoft.com/ja-jp/learn/certifications/azure-ai-fundamentals/">Microsoft Certified: Azure AI Fundamentals</a> ）</p></div>
<p>Microsoft Certified: Azure AI Fundamentalsは国内のクラウドサービスの利用率で2位のMicrosoft Azure（以下Azure）の認定資格です。Microsoft Certified: Azure AI Fundamentalsの資格を獲得するためには試験 AI-900: Microsoft Azure AI Fundamentalsに合格する必要があります。この資格によって評価されるスキルは下記のように設定されています。</p>
<ul>
<li>AIワークロードと考慮事項について説明する</li>
<li>Azureでの機械学習の基本原則について説明する</li>
<li>Azureのコンピュータービジョンワークロードの機能について説明する</li>
<li>Azureの Natural Language Processing (NLP) ワークロードの機能について説明する</li>
<li>Azure での会話型AIワークロードの機能について説明する</li>
</ul>
<p><a href="https://azure.microsoft.com/ja-jp/">Azure</a>とはMicrosoftが提供するクラウドコンピューティングサービスのことです。試験対策としては提供されているラーニングパスを利用して試験に必要な知識を身につけることができます。機械学習の基礎的な知識をAzureを利用して勉強したい方向けの試験です。</p>
<table align="center">
<tbody>
<tr>
<td>主宰</td>
<td>Microsoft Azure</td>
</tr>
<tr>
<td>公式URL</td>
<td><a href="https://docs.microsoft.com/ja-jp/learn/certifications/azure-ai-fundamentals/">https://docs.microsoft.com/ja-jp/learn/certifications/azure-ai-fundamentals/</a></td>
</tr>
<tr>
<td>受験資格</td>
<td>誰でも受験可能</td>
</tr>
<tr>
<td>試験日程</td>
<td>随時</td>
</tr>
<tr>
<td>試験形式</td>
<td>不明</td>
</tr>
<tr>
<td>試験時間</td>
<td>60分</td>
</tr>
<tr>
<td>受験費用</td>
<td>12500円</td>
</tr>
</tbody>
</table>
<h4>Professional Data Engineer</h4>
<div style="width: 248px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2021/08/follow-learning-path.png" alt="" width="238" height="137" /><p class="wp-caption-text">（引用：<a href="https://cloud.google.com/certification/data-engineer?hl=ja">Professional Data Engineer</a>）</p></div>
<p>Professional Data Engineerは国内のクラウドサービスの利用率で2位のGoogle Cloud Platform（以下GCP）の認定資格です。データを収集、変換、公開して、データに基づく意思決定ができるようにすることが求められます。この試験での評価は下記のように設定されています。</p>
<ul>
<li>データ処理システムの設計</li>
<li>データ処理システムの構築と運用化</li>
<li>機械学習モデルの運用化</li>
<li>解決のための情報システムの品質の確保</li>
</ul>
<p>データ分析の基礎技術に加えてGCPの知識も必要になります。そのため、それらの知識を業務で使用されている方、それらの知識を学びたい方向けの試験です。対策本が少ないため、ぜひ以下のリンクを活用して勉強を進めてみてください。また、GCP公式に模擬試験も存在しています。これらもうまく活用して勉強を進めてみてください。（参考：<a href="https://cloud.google.com/solutions/smart-analytics?hl=ja">GCPの公式ドキュメント</a>・<a href="https://www.accenture.com/jp-ja/blogs/cloud-diaries/google-cloud-exam">過去の受験者の記事（引用：アクセンチュア株式会社）</a>・<a href="https://cloud.google.com/certification/sample-questions/data-engineer?hl=ja">模擬試験</a>）</p>
<table align="center">
<tbody>
<tr>
<td>主宰</td>
<td>Google Cloud</td>
</tr>
<tr>
<td>公式URL</td>
<td><a href="https://cloud.google.com/certification/data-engineer?hl=ja">https://cloud.google.com/certification/data-engineer?hl=ja</a></td>
</tr>
<tr>
<td>受験資格</td>
<td>誰でも受験可能</td>
</tr>
<tr>
<td>試験日程</td>
<td>随時</td>
</tr>
<tr>
<td>試験形式</td>
<td>テストセンター又はオンライン</td>
</tr>
<tr>
<td>試験時間</td>
<td>120分</td>
</tr>
<tr>
<td>受験費用</td>
<td>$200</td>
</tr>
</tbody>
</table>
<h3>統計系AI資格</h3>
<p>統計系AI資格では主に統計学の資格を取り上げました。機械学習の急速な進歩に伴い、「統計」という言葉を耳にする機会が増えたと思います。数学的な内容が主になるため、苦手意識を持たれる方も多いかもしれません。しかし、統計は機械学習の根本を理解する上で重要な知識です。特に、データサイエンティストを目指される方には重要な分野になるため、確実に押さえておきましょう。</p>
<h4>統計検定</h4>
<div style="width: 405px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2021/03/title_181102.png" alt="" width="395" height="112" /><p class="wp-caption-text">（引用：<a href="https://www.toukei-kentei.jp/">統計検定</a>）</p></div>
<p>統計検定は、一般財団法人 統計質保証推進協会が主宰している統計に関する知識や活用力を認定するための試験です。4級、3級、2級、準1級、1級と難易度が別れているため、自分にあった難易度からスタートすることができます。統計検定は、統計に関する知識や活用力を認定するための試験です。最近では本稿を含む多くのネット上の記事で、統計検定がAI資格として紹介されています。</p>
<p>データ分析では機械学習を使用する前段階で、統計的な見方から正しくデータを読み取ることが要求されます。そのため、統計学は確実に理解する必要があるのです。統計検定はそうしたデータ分析を行い、機械学習に繋げたい方、データサイエンティストになりたい方向けの試験です。</p>
<p>AIの人気により統計検定を知った方もいると思います。しかし、統計検定自体は2011年に発足、G検定の6年前から存在しています。元は大学における統計教育の成果と統計分野の能力を測る手段として構想されました。それほど前から統計分野の重要性は指摘されていたということです。AIブームにより、統計学がAIの派生として存在しているように誤認されることがあります。本稿を読んでいる皆様には統計学がAIの基礎部分に存在するということをご理解頂けたら幸いです。</p>
<p>過去問題や公式問題集も存在するため、比較的対策は行いやすいです。しかし、2021年度を持って1級以外は、従来の試験形式であるPBT方式を廃止し、全面的にCBT方式に移行します。そのため、試験問題や出題形式の変化などには十分に注意してください。（参考：<a href="https://www.toukei-kentei.jp/past/">過去問題</a>・<a href="https://www.toukei-kentei.jp/info/books/">公式問題集</a>）</p>
<table align="center">
<tbody>
<tr>
<td>主宰</td>
<td>一般財団法人 統計質保証推進協会</td>
</tr>
<tr>
<td>公式URL</td>
<td><a href="https://www.toukei-kentei.jp/">https://www.toukei-kentei.jp/</a></td>
</tr>
</tbody>
</table>
<p>＞<a href="https://www.toukei-kentei.jp/">統計検定（PBT方式）公式</a><br />
＞<a href="https://www.toukei-kentei.jp/cbt/">統計検定（CBT方式）公式</a></p>
<h5>統計検定 ４級</h5>
<p>4級は「データと表やグラフ、確率に関する基本的な知識と具体的な文脈の中で求められる統計活用力を評価し、認証するために検定」と記載されています。2019年11月の試験での受験者数は422名、合格率は56.2%でした。統計検定の中では最も初歩的な試験であるため、統計リテラシーの基礎を学びたい方、初めて統計検定を受験されたい方向けの試験です。4級では主にグラフの見方やデータの代表値などの統計学の基礎的な部分が出題されます。</p>
<table align="center">
<tbody>
<tr>
<td>受験資格</td>
<td>誰でも受験可</td>
</tr>
<tr>
<td>試験日程</td>
<td>随時</td>
</tr>
<tr>
<td>試験形式</td>
<td>会場受験（CBT方式）</td>
</tr>
<tr>
<td>試験時間</td>
<td>60分</td>
</tr>
<tr>
<td>受験費用</td>
<td>一般：5000円/学生：3500円</td>
</tr>
</tbody>
</table>
<p>＞<a href="https://www.toukei-kentei.jp/cbt/cbt_about/grade4/">4級の概要（CBT方式）</a></p>
<h5>統計検定 ３級</h5>
<p>3級は「大学基礎統計学の知識として求められる統計活用力を評価し、認証するために検定」と記載されています。2019年11月の試験での受験者数は1907人、合格率は61.8%でした。4級に合格した方、大学などで少し統計に触れている方向けの試験です。3級では2級の内容に加え、相関係数や回帰直線、正規分布などの内容が含まれます。</p>
<table align="center">
<tbody>
<tr>
<td>受験資格</td>
<td>誰でも受験可</td>
</tr>
<tr>
<td>試験日程</td>
<td>随時</td>
</tr>
<tr>
<td>試験形式</td>
<td>会場受験（CBT方式）</td>
</tr>
<tr>
<td>試験時間</td>
<td>60分</td>
</tr>
<tr>
<td>受験費用</td>
<td>一般：6000円/学生：4000円</td>
</tr>
</tbody>
</table>
<p>＞<a href="https://www.toukei-kentei.jp/cbt/cbt_about/grade3/">3級の概要（CBT方式）</a></p>
<h5>統計検定 ２級</h5>
<p>2級は「大学基礎課程（１・２年次学部共通）で習得すべきことについての検定」と記載されています。統計検定を受験される方の中には2級から受験し始める方も多くいます。2019年11月の試験では受検者数が2369人と統計検定の中で最多でした。また同試験での合格率は41.7%となっています。大学で基礎統計学を学んでいる方、データ分析に普段から触れている方向けの試験です。2級では3級の内容に加え、ベイズの定理やカイ二乗検定、t分布などの内容が含まれます。</p>
<table align="center">
<tbody>
<tr>
<td>受験資格</td>
<td>誰でも受験可</td>
</tr>
<tr>
<td>試験日程</td>
<td>随時</td>
</tr>
<tr>
<td>試験形式</td>
<td>会場受験（CBT方式）</td>
</tr>
<tr>
<td>試験時間</td>
<td>90分</td>
</tr>
<tr>
<td>受験費用</td>
<td>一般：7000円/学生：5000円</td>
</tr>
</tbody>
</table>
<p>＞<a href="https://www.toukei-kentei.jp/cbt/cbt_about/grade2/">2級の概要（CBT方式）</a></p>
<h5>統計検定 準１級</h5>
<p>準1級は「大学において統計学の基礎的講義に引き続いて学ぶ応用的な統計学の諸手法の習得について検定」と記載されています。2019年6月の試験では受験者数が853人、合格率が21%と2級に比べて難易度が大幅に上がっています。大学で統計を専門的に学んだ方、統計スキルを証明したい方向けの試験です。準1級では2級までの基礎知識を用いて実社会の問題を例に適切な統計学の手法の選択が求められます。</p>
<table align="center">
<tbody>
<tr>
<td>受験資格</td>
<td>誰でも受験可</td>
</tr>
<tr>
<td>試験日程</td>
<td>年1回（2021年からCBT方式開始）</td>
</tr>
<tr>
<td>試験形式</td>
<td>会場受験（2021年からCBT方式開始）</td>
</tr>
<tr>
<td>試験時間</td>
<td>120分</td>
</tr>
<tr>
<td>受験費用</td>
<td>8000円</td>
</tr>
</tbody>
</table>
<p>＞<a href="https://www.toukei-kentei.jp/about/grade1semi/">準1級の概要</a></p>
<h5>統計検定 １級</h5>
<p>1級は「大学専門課程（３・４年次）で習得すべきことについて、専門分野ごとに検定」と記載されています。準1級までとは異なり、試験が「統計数理」と「統計応用」の2つに別れます。1級の取得には2つの試験で合格点を取ることが必要になります。2019年11月の試験での受験者数は「統計数理」が878名,「統計応用」が793名、合格率は「統計数理」が23%,「統計応用」が15.8%でした。難易度が非常に高いため、大学で統計を専門的に学んだ方、統計を実務で使用されている方向けの試験です。</p>
<p>1級の「統計数理」では5問出題された中から3問を選択します。「統計応用」では人文科学、社会科学、理工学、医薬生物学の4分野に問題が別れており、1分野を選択します。その上で、選択した分野で5問出題され、3問を選択します。そのため、1級を受験される方は分野ごとの問題内容を確認し、どの分野を選択するか検討する必要があります。</p>
<table align="center">
<tbody>
<tr>
<td>受験資格</td>
<td>誰でも受験可</td>
</tr>
<tr>
<td>試験日程</td>
<td>年1回</td>
</tr>
<tr>
<td>試験形式</td>
<td>会場受験</td>
</tr>
<tr>
<td>試験時間</td>
<td>「統計数理」：90分/「統計応用」：90分</td>
</tr>
<tr>
<td>受験費用</td>
<td>10000円/（片方のみは6000円）</td>
</tr>
</tbody>
</table>
<p><a href="https://www.toukei-kentei.jp/about/grade1/">1級の概要（PBT方式）</a></p>
<h4>統計士（現代統計実務講座）</h4>
<div style="width: 715px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2021/08/statistics_main.jpeg" alt="" width="705" height="159" /><p class="wp-caption-text">（引用：<a href="https://www.jitsumu.or.jp/courselist/statistics">現代統計実務講座</a>）</p></div>
<p>現代統計実務講座は文部科学省に認定された統計学分野の通信講座です。8つの単元を1ヶ月ごとに学習します。各単元の報告課題と報告課題と週末試験に合格することで、現代統計実務講座の修了証明書と統計士の認定を受けることができます。統計初心者が対象になっているため、時間をかけて確実に統計の知識を身に付けたい方向けの講座です。</p>
<table align="center">
<tbody>
<tr>
<td>主宰</td>
<td>一般財団法人　実務教育研究所</td>
</tr>
<tr>
<td>公式URL</td>
<td><a href="https://www.jitsumu.or.jp/courselist/statistics">https://www.jitsumu.or.jp/courselist/statistics</a></td>
</tr>
<tr>
<td>受験資格</td>
<td>誰でも受験可能</td>
</tr>
<tr>
<td>試験日程</td>
<td>随時</td>
</tr>
<tr>
<td>試験形式</td>
<td>通信講座</td>
</tr>
<tr>
<td>講座期間</td>
<td>標準学習期間8ヶ月</td>
</tr>
<tr>
<td>講座費用</td>
<td>60140円（一括払い）</td>
</tr>
</tbody>
</table>
<h4>データ解析士（多変量解析実務講座）</h4>
<div style="width: 715px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2021/08/h-img-tahenryou.jpeg" alt="" width="705" height="159" /><p class="wp-caption-text">（引用：<a href="https://www.jitsumu.or.jp/courselist/analyze">多変量解析実務講座</a>）</p></div>
<p>多変量解析実務講座は文部科学省に認定された多変量解析分野の通信講座です。4つの単元を1ヶ月ごとに学習します。各単元の報告課題と終末試験に合格することで、多変量解析実務講座の修了証明書を取得することができます。その後、認定試験合格者はデータ解析士の認定を受けることができます。統計中級者が対象となっており、統計技法を活用したいと考えている方向けの講座です。</p>
<p>多変量解析実務講座は現代統計実務講座の1段上のレベルです。現代統計実務講座ではパソコンや統計ソフトを使用せずに受講できるのに対して、多変量解析実務講座は主にExcelを使用して学習を進めます。また、多変量解析実務講座では回帰分析の基礎から応用を集中的に学習します。</p>
<table align="center">
<tbody>
<tr>
<td>主宰</td>
<td>一般財団法人　実務教育研究所</td>
</tr>
<tr>
<td>公式URL</td>
<td><a href="https://www.jitsumu.or.jp/courselist/analyze">https://www.jitsumu.or.jp/courselist/analyze</a></td>
</tr>
<tr>
<td>受験資格</td>
<td>誰でも受験可能</td>
</tr>
<tr>
<td>試験日程</td>
<td>随時</td>
</tr>
<tr>
<td>試験形式</td>
<td>通信講座</td>
</tr>
<tr>
<td>講座期間</td>
<td>標準学習期間4ヶ月</td>
</tr>
<tr>
<td>講座費用</td>
<td>54840円（一括払い）</td>
</tr>
</tbody>
</table>
<h3>その他AI資格</h3>
<p>「その他AI資格」では「Python3エンジニア認定データ分析試験」と「画像処理エンジニア検定」を取り上げました。どちらの資格も機械学習を勉強する上で重要な領域をカバーします。機械学習の勉強をしている中で、よりPythonでの実装力を身に付けたい方や、画像分野への応用を考えている方は是非確認してみてください。</p>
<h4>Python 3 エンジニア認定データ分析試験</h4>
<p><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2021/08/logo.png" alt=" width=" height="100" /></p>
<p>Python3エンジニア認定データ分析試験はPythonを使ったデータ分析の基礎や方法を問う試験です。Pythonは機械学習を勉強していく上で重要なプログラミング言語です。特に、Pythonで使用される機械学習向けライブラリを使用できるようになることは必須スキルと言えます。そのため、Pythonの基礎やライブラリを使用したデータ分析の基礎を身に付けたいという方に人気の試験です。</p>
<p>問題数は40問、合格ラインは正答率が70%以上です。主教材も公式から発表されており、「Pythonによるあたらしいデータ分析の教科書」が推奨されています。そのほかにも認定スクールであるPRIME STUDY（プライム・スタディ）が模擬試験を提供しているので参考にしてみてください。（参考：<a href="https://www.amazon.co.jp/dp/4798158348/">Pythonによるあたらしいデータ分析の教科書</a>・<a href="https://study.prime-strategy.co.jp/">PRIME STUDY（プライム・スタディ）</a>）</p>
<table align="center">
<tbody>
<tr>
<td>主宰</td>
<td>一般社団法人Pythonエンジニア育成推進協会</td>
</tr>
<tr>
<td>公式URL</td>
<td><a href="https://www.pythonic-exam.com/exam/analyist">https://www.pythonic-exam.com/exam/analyist</a></td>
</tr>
<tr>
<td>受験資格</td>
<td>誰でも受験可能</td>
</tr>
<tr>
<td>試験日程</td>
<td>随時</td>
</tr>
<tr>
<td>試験形式</td>
<td>テストセンター（CBT方式）</td>
</tr>
<tr>
<td>試験時間</td>
<td>60分</td>
</tr>
<tr>
<td>試験費用</td>
<td>一般：10000円/学生：5000円</td>
</tr>
</tbody>
</table>
<h4>画像処理エンジニア検定</h4>
<div style="width: 812px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2021/08/tit_index.jpeg" alt="" width="802" height="84" /><p class="wp-caption-text">（引用：<a href="https://www.cgarts.or.jp/kentei/about/img_engineer/index.html">画像処理エンジニア検定</a>）</p></div>
<p>画像処理エンジニア検定は公益財団法人画像情報教育振興協会（CG-ARTS）による、画像処理分野の開発、設計に必要な知識の習得を評価する検定です。画像処理分野は機械学習、特にディープラーニングの領域で非常に注目を集めています。そこでは、画像分類、物体検出、画像生成など様々な応用方法が期待されています。しかし、画像処理分野は必要とされる基礎知識が多いため、別途学習が必要です。そのため、画像処理エンジニア検定は機械学習で画像処理分野に挑戦したい方や画像処理分野にすでに馴染みのある方向けの試験です。</p>
<p>レベルはベーシックとエキスパートに別れており、それぞれ年2回開催されています。過去2年分の試験問題や対応書籍も掲載されているため、これらを活用しながら勉強することができます。（参考：<a href="https://www.cgarts.or.jp/kentei/past/index.html">過去2年分の試験問題</a>・<a href="https://www.cgarts.or.jp/book/img_engineer/index.html">対応書籍</a>）</p>
<table align="center">
<tbody>
<tr>
<td>主宰</td>
<td>公益財団法人画像情報教育振興協会（CG-ARTS）</td>
</tr>
<tr>
<td>公式URL</td>
<td><a href="https://www.cgarts.or.jp/kentei/about/img_engineer/index.html">https://www.cgarts.or.jp/kentei/about/img_engineer/index.html</a></td>
</tr>
</tbody>
</table>
<h5>画像処理エンジニア検定 ベーシック</h5>
<p>ベーシックでは画像処理の基礎的な内容を問います。2020年度後期の試験では受験者数は677人、合格率は68.95%であったため難易度は比較的低めです。そのため、画像処理に興味を持たれている方や、画像処理エンジニア検定を初めて受ける方向けの試験です。</p>
<table align="center">
<tbody>
<tr>
<td>受験資格</td>
<td>誰でも受験可能</td>
</tr>
<tr>
<td>試験日程</td>
<td>年2回</td>
</tr>
<tr>
<td>試験形式</td>
<td>会場受験</td>
</tr>
<tr>
<td>試験時間</td>
<td>60分</td>
</tr>
<tr>
<td>試験費用</td>
<td>5600円</td>
</tr>
</tbody>
</table>
<p>＞<a href="https://www.cgarts.or.jp/kentei/about/img_engineer/third.html">ベーシックの出題範囲</a></p>
<h6>画像処理エンジニア検定 アドバンス</h6>
<p>アドバンスは画像処理の専門的な知識と応用を問う試験です。ベーシックよりも難易度は高く、2020年度後期の試験では受験者数は557人、合格率は42.83%でした。そのため、画像処理の分野をすでに勉強されている方や、ベーシックに合格された方向けの試験です。</p>
<table align="center">
<tbody>
<tr>
<td>受験資格</td>
<td>誰でも受験可能</td>
</tr>
<tr>
<td>試験日程</td>
<td>年2回</td>
</tr>
<tr>
<td>試験形式</td>
<td>会場受験</td>
</tr>
<tr>
<td>試験時間</td>
<td>80分</td>
</tr>
<tr>
<td>試験費用</td>
<td>6700円</td>
</tr>
</tbody>
</table>
<p>＞<a href="https://www.cgarts.or.jp/kentei/about/img_engineer/second.html">エキスパートの出題範囲</a></p>
<h2>英語で受験可能なAI資格一覧</h2>
<p>国内だけでなく海外にもAI資格は多く存在します。海外の資格では英語の受験が求められるため、日本語での受験に対してレベルが上がります。そのため、英語力に自信のある方や、もう一段レベルの高い機械学習エンジニアを目指している方は是非挑戦してみてください。</p>
<p>海外資格は国内の資格と違い、試験形式ではなくコース形式になっている場合が多いです。1つの認定証明書を得るために複数のコースの修了が必要な場合もあります。そのため、金額が高額になったり、学習時間が数ヶ月に及ぶ場合があります。ご自身で受講されたいコースをよくご確認してから受講しましょう。</p>
<p>本稿では下記に英語で資格可能な資格・講座を記載しました。下記以外にもAI資格は多数存在するので興味がある方はご自身で調べてみてください。</p>
<table align="center">
<tbody>
<tr>
<td>資格名</td>
<td>主宰</td>
<td>概要</td>
</tr>
<tr>
<td><a href="https://docs.microsoft.com/ja-jp/learn/certifications/azure-ai-engineer/">Microsoft Certified: Azure AI Engineer Associate</a></td>
<td>Microsoft</td>
<td><a href="https://docs.microsoft.com/ja-jp/learn/certifications/azure-ai-fundamentals/">Microsoft Certified: Azure AI Fundamentals</a>の上位資格です。機械学習の基礎的な内容に加えて自然言語や会話型AIをAzureに実装する能力が求められます。</td>
</tr>
<tr>
<td><a href="https://cloud.google.com/certification/machine-learning-engineer">Professional Machine Learning Engineer</a></td>
<td>Google Cloud</td>
<td>機械学習分野の人材不足を目指して作られた認定資格です。機械学習手法の設計からモデル開発まで、専門的な能力が評価されます。</td>
</tr>
<tr>
<td><a href="https://www.coursera.org/specializations/machine-learning-tensorflow-gcp">Machine Learning with TensorFlow on Google Cloud Platform Specialization</a></td>
<td>Google Cloud</td>
<td><a href="https://www.coursera.org/">Coursera</a>で提供されている機械学習のコースです。GCPでTnsorflowを実装しながらニューラルネットワークの理論から実装までを学びます。</td>
</tr>
<tr>
<td><a href="https://professional.mit.edu/programs/certificate-programs/professional-certificate-program-machine-learning-artificial">Professional Certificate Program in Machine Learning and Artificial Intelligence</a></td>
<td>MIT Professional Education</td>
<td>MITにより提供されている機械学習の認定プログラムです。コース毎に日数が設定され、16日分のコースを学習することで証明書を授与されます。</td>
</tr>
<tr>
<td><a href="https://online.stanford.edu/courses/cs229-machine-learning">Machine Learning Stanford Online</a></td>
<td>Stanford Univercity</td>
<td>Stanfordが提供しているオンラインコースです。近年のアプリケーション開発に触れながら、機械学習と統計的パターン認識について幅広く学習します。</td>
</tr>
<tr>
<td><a href="https://www.edx.org/professional-certificate/berkeleyx-foundations-of-data-science">Professional Certificate in Foundations Of Data Science</a></td>
<td>Berkeley UNIVERCITY OF CALIFORNIA</td>
<td>Berkeleyが提供しているデータサイエンスのコースです。様々な事例からデータ分析及び機械学習の適用を学習します。同時にPythonによる実装も学びます。</td>
</tr>
<tr>
<td><a href="https://www.ecornell.com/certificates/technology/machine-learning/">MACHINE LEARNING Cornell Certificate Program</a></td>
<td>Corenell</td>
<td>Cornellが提供する機械学習のコースです。統計学の基礎からディープラーニングまでをPythonを使用しながら学んびます。</td>
</tr>
<tr>
<td><a href="https://www.pce.uw.edu/certificates/machine-learning">Certificate in Machine learning</a></td>
<td>UNIVERCITY OF WASHINTON</td>
<td>UNIVERCITY OF WASHINTONによって提供される機械学習のコースです。機械学習を数学的側面と応用的側面の両方から学習し、機械学習エンジニアに必要な能力を学びます。3つのコースを修了することで、プログラムの証明書が授与されます。</td>
</tr>
<tr>
<td><a href="https://www.edx.org/professional-certificate/harvardx-data-science">Professional Certificate<br />
Data Science</a></td>
<td>HARVARD UNIVERCITY</td>
<td><a href="https://www.edx.org/">edX</a>で提供されるHarvardのデータサイエンスコースです。統計、機械学習までを網羅的に学習することができます。また、プログラミング言語はPythonではなく、Rを使用します。</td>
</tr>
<tr>
<td><a href="https://www.coursera.org/professional-certificates/ai-engineer">IBM AI Engineering Professional Certificate</a></td>
<td>IBM</td>
<td><a href="https://www.coursera.org/">Coursera</a>で提供されるIBMのAIエンジニアプロフェッショナル認定です。Pythonを使用して機械学習の概念からディープラーニングまでを幅広く学習します。6つのコースを修了することで、証明書が授与されます。</td>
</tr>
<tr>
<td><a href="https://www.coursera.org/professional-certificates/ibm-data-science">IBM Data Science Professional Certificate</a></td>
<td>IBM</td>
<td><a href="https://www.coursera.org/">Coursera</a>で提供されるIBMのデータサイエンスプロフェッショナル認定です。Pythonを使用して統計分析から予測モデリングまでデータサイエンス分野を中心に学習します。9つのコースを修了することで、証明書が授与されます。</td>
</tr>
</tbody>
</table>
<h3>海外資格取得のメリット</h3>
<p>ここまで、日本語で受けられるAI資格と英語で受けられるAI資格を見てきました。恐らく、多くの方が国内資格を中心に受験しようと考えているかと思います。当然、海外資格は英語力が必須になるため、難易度が必然的に上がります。しかし海外資格にもメリットは存在します。ここではそのメリットを大きく２つに分けてご紹介します。</p>
<p>1.英語力の証明</p>
<p>英語力の証明と聞くと多くの方が、TOEICや英語検定を思い浮かべると思います。しかし、AIや機械学習の分野で使用される専門用語は通常の英会話では使用される機会はほとんどありません。海外資格を取得していれば、通常の英会話に加えて、その分野の話が通じる相手として評価してもらえます。国内資格ではこのような評価を受けることはできません。そのため、英語力と専門性の２つを同時に証明できることは大きなメリットです。</p>
<p>2.最新情報のキャッチアップが早くなる</p>
<p>AIや機械学習の情報は基本的に英語で記載されている場合が多いです。ライブラリの公式ドキュメントや、技術書などが該当します。日本語で記載されているものも存在しますが、それらは英語で記載された情報を元にしている場合がほとんどです。海外のAI資格を勉強している過程で、AI関連の情報を英語で取得できるようになれば、最先端の技術や論文をいち早くキャッチアップすることができるようになります。近年の機械学習分野の進歩は非常に早いです。その進歩についていけるようになることは非常に大きなアドバンテージを生み出します。</p>
<h2>まとめ</h2>
<p>いかがでしたでしょうか。本稿を通して、みなさんが自分にあったAI資格を見つけることができれば幸いです。資格だけでは機械学習エンジニアになることはできません。しかし、知識を得るための手段としてや、短期的な目標として、資格を友好的に活用することをオススメします。</p>
<p>codexaでは機械学習の勉強を簡単に始められるコースをご用意しています。是非、受講をお待ちしております。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.codexa.net/ai_certification/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>OpenCV 入門：画像処理・画像認識・機械学習の実装を徹底解説（全実装コード公開）</title>
		<link>https://www.codexa.net/opencv_python_introduction/</link>
					<comments>https://www.codexa.net/opencv_python_introduction/#respond</comments>
		
		<dc:creator><![CDATA[codexaチーム]]></dc:creator>
		<pubDate>Tue, 09 Mar 2021 05:40:04 +0000</pubDate>
				<category><![CDATA[機械学習]]></category>
		<guid isPermaLink="false">https://www.codexa.net/?p=2796</guid>

					<description><![CDATA[OpenCVとは画像処理と画像認識の両方を実装できるPythonのライブラリです。本稿はOpenCVの主な機能や他の画像系ライブラリとの違いを解説し、後半は画像処理、物体検出（画像認識）、機械学習実装をPythonを用いて行います。]]></description>
										<content:encoded><![CDATA[<p>皆さん、突然ですが上の画像をご覧ください。<br />
（画像引用：<a href="https://opencv.org/multiple-object-tracking-in-realtime/">OpenCV公式サイト</a>より）</p>
<p>これは、画像認識技術の応用例の一つです。コンピューターが映像の範囲内で移動する複数の人間を検知し、その移動をリアルタイムで追跡している様子が見て取れます。かなりの数の人がいるにも関わらず、ほとんどの人を正確に検出することができていますね。画像認識とは、このように画像の数値情報のパターンから写っている物体を認識する手法のことを指します。（本稿で度々登場する物体検出という用語も画像認識と同様の意味を持つと考えていいでしょう。）</p>
<p>また、これは画像認識の例ですが、高精度の画像認識を行うために必要不可欠なのが画像処理の技術です。画像処理とは、コンピュータを用いて画像に対して行う、色を変換したり変形させたり、ぼかしたりするなどの処理全般を指します。画像処理を行うことで、コンピュータが画像内の物体を認識しやすくなります。</p>
<p>そして、本稿で扱うOpenCV（読み：オープン・シーブイ）は、画像処理と画像認識の両方を実装できるPythonのライブラリです。前半で、OpenCVについて、主な機能や他の画像系ライブラリとの違いを解説します。後半では、OpenCVを用いて実際に「画像処理」、「物体検出（画像認識）」、「機械学習」の実装を行います。本稿を通して、画像処理・画像認識の基礎知識と実装スキルを身につけましょう。</p>
<h3>OpenCVとは</h3>
<p>本節では、OpenCVについて詳しく解説していきます。OpenCVの概要について説明したのち、OpenCVの主な機能を紹介します。また、PillowやScikit-imageなどの他の画像処理系ライブラリとの比較も行います。</p>
<div style="width: 395px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2021/01/スクリーンショット-2021-01-06-13.25.37.png" alt="" width="385" height="186" /><p class="wp-caption-text">＜<a href="https://opencv.org/">OpenCV公式サイト</a>より＞ ※背景の挿入は筆者による</p></div>
<h4>OpenCVの概要</h4>
<p>OpenCVは、その名の通りオープンソースのコンピュータビジョン用ライブラリです。コンピュータビジョンは、コンピュータによる視覚についての研究分野の名称ですが、画像認識とほぼ同義と考えていただければわかりやすいかと思います。OpenCVは、元々はインテルが開発したプログラムで、現在でも開発が進められています。ごく一部のアルゴリズムは特許を取得されているため、それらを商用利用する際には確認が必要ですが、基本的に無料で利用することができ、商用利用も可能です。また、クロスプラットフォームのライブラリで、Linux、MacOS、Windowsや、iOS、AndroidといったOSとC++、Python、Javaの三つのプログラミング言語に対応しているため、あまり環境の制約を受けることなく活用することができます。</p>
<h4>OpenCVの主な機能</h4>
<p>OpenCVの主な機能について見ていきましょう。本稿では、OpenCVの主な機能を以下の５つに大別して解説します。</p>
<ul>
<li>画像と動画の読み込み / 表示 / 保存</li>
<li>画像処理</li>
<li>機械学習</li>
<li>検出（画像認識）</li>
<li>三次元の処理</li>
</ul>
<h5>画像と動画の読み込み・表示・保存</h5>
<p>画像を扱うためには画像を表示したり保存したりする機能がなければいけません。OpenCVにはこれらの基本的な機能は当然搭載されています。また、動画を扱うための機能や、図形や文字などを描画する機能もあり、様々な素材を扱うことができます。</p>
<h5>画像処理</h5>
<p>OpenCVで実行可能な画像処理の例として以下のようなものが挙げられます。本稿後半の実装では、犬の画像を用いて様々な画像処理を行いますので、ご期待ください。</p>
<ul>
<li><strong><span style="text-decoration: underline;">色変換</span></strong><br />
カラー画像をグレースケール画像にしたり、BGR（Blue, Green, Red）で表示されている画像の色をRGB（Red, Green, Blue）に変換したりと、様々な変換方法があります。単に色を変換するだけでなく、他の処理と組み合わせることによって、画像内にある特定の色の物体を検出することなどもできます。</li>
<li><strong><span style="text-decoration: underline;">幾何変換（拡大・縮小・回転など）</span></strong><br />
画像を拡大・縮小したり、回転させたりすることができます。一見大したことのない処理に思えますが、物体検出の際に画像の向きや大きさなどを変えるだけでも検出の成否が変わることもあるため、重要な処理手法です。</li>
<li><span style="text-decoration: underline;"><strong>しきい値処理</strong></span><br />
グレースケール化された画像において、設定されたしきい値よりピクセルの値が大きい場合と小さい場合で別々の値を割り当てることで画像を変換する手法です。説明だけではイメージしづらいかと思いますので、後半の実装を確認してください。</li>
<li><span style="text-decoration: underline;"><strong>平滑化</strong></span><br />
平滑化は、端的に言えばぼかしを入れる処理手法です。ぼかしの入れ方にも様々な手法があり、適切な手法を用いることで画像内のノイズを除去することができます。</li>
<li><span style="text-decoration: underline;"><strong>モルフォロジー変換</strong></span><br />
主に色が二種類の画像（二値画像）に対して行う処理で、画像上に写っている物体を膨張させたり縮小させたりすることができます。これも、平滑化と同様にノイズ除去に活用できます。</li>
<li><span style="text-decoration: underline;"><strong>部分的な復元</strong></span><br />
落書きや傷がある写真などにおいて、周辺の画素の情報からその部分を復元するといった処理も可能です。</li>
</ul>
<h5>機械学習</h5>
<p>OpenCVには、機械学習の機能も含まれています。OpenCVで活用できる手法はk近傍法、<a href="https://www.codexa.net/support-vector-machine-tutorial/">サポートベクターマシン</a>、K-Meansクラスタリングで、これらの手法を文字分類や画像処理に活用することができます。例えば、クラスタリングの手法を用いて画像に含まれる色のデータを複数のクラスターに分けることで、色の種類を減らす処理なども可能です。本稿後半の実装では、k近傍法を用いてくずし字の分類予測を行います。</p>
<h5>検出（画像認識）</h5>
<p>画像中の様々なものを検出・認識する機能としては以下のようなものがあります。</p>
<ul>
<li><span style="text-decoration: underline;"><strong>直線・円の検出</strong></span></li>
<li><span style="text-decoration: underline;"><strong>エッジ検出</strong></span><br />
エッジとは、輝度が大きく変化する点のことで、物体の輪郭などがそれに当たります。</li>
<li><span style="text-decoration: underline;"><strong>物体検出</strong></span><br />
画像中の物体を検出するいわゆる画像認識の機能です。顔や目など、基本的な物体については分類器がOpenCV内に用意されている上、学習に必要な大量の画像データさえ集めることができれば、自分で検出したい物体用の分類器を作ることもできます。</li>
</ul>
<h5>三次元の処理</h5>
<p>さらに、OpenCVでは二次元の画像データから三次元の情報を推測し、以下のような処理をすることができます。</p>
<ul>
<li>画像の歪みの補正</li>
<li>画像中に三次元の物体を描画</li>
<li>複数枚の二次元画像から実際の距離を推測</li>
</ul>
<p>以上がOpenCVの主な機能です。ここに紹介しているものだけでも様々なことができますが、これでもOpenCVの機能の一部にすぎません。本稿では簡単にご説明しましたが、各機能の仕組みも含めてより詳しく知りたい方は、<a href="http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_tutorials.html">OpenCV-Pythonのチュートリアル</a>をご覧いただくといいかと思います。</p>
<h4>他の主な画像処理系ライブラリとの比較</h4>
<p>OpenCVがいかに幅広い機能を備えているかは概ねお分かりいただけたかと思います。しかし、画像処理・画像認識系のライブラリには他にもPillow（読み：ピロウ）やScikit-image（読み：サイキット・イメージ）など有名なものがあります。では、これらのライブラリとOpenCVにはいったいどのような違いがあるのでしょうか。各ライブラリの特徴を確認し、OpenCVと比較してみましょう。</p>
<h5 style="text-align: center;">Pillow</h5>
<div style="width: 411px" class="wp-caption aligncenter"><img loading="lazy" style="font-size: 16px;" src="https://www.codexa.net/wp-content/uploads/2021/01/スクリーンショット-2021-01-06-13.25.51-e1609907399882.png" alt="" width="401" height="159" /><p class="wp-caption-text">＜<a href="https://pillow.readthedocs.io/en/stable/index.html">Pillow公式サイト</a>より＞ ※背景の挿入は筆者による</p></div>
<p>PillowはPython Imaging Library (PIL)から派生したPython用画像処理ライブラリです。サムネイル画像の作成、画像のファイル形式の変更、画像の表示などの基本的な操作に加えて、画像のリサイズ、回転、アフィン変換、色変換、明るさの調整、二値変換、ぼかし、減色、画像の貼り付けなど、様々な処理を行うことができます。ただし、あくまで画像処理ライブラリであり、OpenCVのような機械学習や物体検出の機能は搭載されていないようです。画像処理だけが目的であればPillowを使ってみるのもいいでしょう。</p>
<h5 style="text-align: center;">Scikit-image</h5>
<div style="width: 452px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2021/01/スクリーンショット-2021-01-06-13.25.30.png" alt="" width="442" height="153" /><p class="wp-caption-text">＜<a href="https://scikit-image.org/">scikit-image公式サイト</a>より＞</p></div>
<p>Scikit-imageは画像処理や物体検出のためのPython用ライブラリです。OpenCVやPillowと同様の様々な画像処理に加えて、画像内の特徴点や物体を検出することもできます。ただし、機械学習を行う際には他のライブラリを用いる必要があります。</p>
<h5 style="text-align: center;">NumPy</h5>
<div style="width: 383px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2021/01/スクリーンショット-2021-01-06-13.26.01.png" alt="" width="373" height="166" /><p class="wp-caption-text">＜<a href="https://numpy.org/">NumPy公式サイト</a>より＞</p></div>
<p>画像データは数値データの集まりなので、数値計算ライブラリとしてよく知られているNumPy（読み：ナンパイ）も画像処理に活用することができます。単色化や減色などの処理が可能ですが、NumPyだけでは画像を読み込めないため、OpenCVやPillowといった画像の読み込みが可能なライブラリと併用する必要があります。（参考：<a href="https://www.codexa.net/numpy/">NumPy入門コース</a>）</p>
<p>Pillow、Scikit-image、NumPyなど、画像処理や画像認識の際に便利なライブラリがいくつもあることがお分かりいただけたと思います。その上で、OpenCVを用いるメリットの一つとして、画像処理、物体検出、そして機械学習までを一つのライブラリで行うことができる点が挙げられます。次節の実装ではこの特徴を活かし、OpenCVをフル活用して様々な実装をしていきましょう。</p>
<h3>OpenCVによる画像処理、画像認識実践</h3>
<p>本節では、いよいよOpenCVを用いて画像処理と画像認識を行います。具体的には、画像処理、物体検出、機械学習を用いた文字分類の順で実装・解説していきます。なお、本稿の実装においては、Google Colabを利用します。（参考：<a href="https://www.codexa.net/how-to-use-google-colaboratory/">Google Colabの使い方</a>）</p>
<p>まずは利用するライブラリをまとめてインポートしましょう。numpy、pandas、matplotlibに加えて、OpenCVのpython用ライブラリであるcv2をインポートします。また、OpenCVには、画像を表示する際に用いるcv2.imshowというメソッドがありますが、Google Colabで使うとエラーとなってしまうので、パッチとして用意されているcv2_imshowをインポートしておきましょう。</p><pre class="crayon-plain-tag">[In] :

#ライブラリのインポート
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import cv2
from google.colab.patches import cv2_imshow</pre><p>
<h4>画像処理</h4>
<p>実際に画像処理を行う前に、画像表示用の関数を定義しておきます。本稿の実装では、様々な画像処理を行うため、次の関数を用いて画像表示を簡略化します。この関数の引数に（元の画像、処理後の画像）を指定することで、元の画像と処理後の画像を二つ並べて表示することができます。</p><pre class="crayon-plain-tag">[In] :

#画像を表示するための関数を定義
def show_img(Input,Output):
    plt.subplot(121) #画像の位置を指定
    plt.imshow(Input) #画像を表示
    plt.title('Input') #画像の上にInputと表記
    plt.xticks([]) #x軸の目盛りを非表示
    plt.yticks([]) #y軸の目盛りを非表示
    
    plt.subplot(122) #画像の位置を指定
    plt.imshow(Output) #画像を表示
    plt.title('Output') #画像の上にOutputと表記
    plt.xticks([]) #x軸の目盛りを非表示
    plt.yticks([]) #y軸の目盛りを非表示</pre><p>
<h5>画像の読み込み</h5>
<p>まずはGoogle Colab上にアップロードした画像を読み込みます。本稿では、画像処理の実装のために犬の画像を利用します。同じ画像で実装を行いたい方は<a href="https://pixabay.com/ja/photos/%E7%8A%AC-%E5%8B%95%E7%89%A9-%E5%AD%90%E7%8A%AC-%E3%83%9A%E3%83%83%E3%83%88-4390885/">こちらのサイト</a>からダウンロードして、Google Colab上にアップロードしてください。画像の読み込みにはcv2.imreadを用い、読み込みの際には第二引数に０を指定することでグレースケール化を行います。グレースケール化とは、単純に言えば画像を白黒にし、ピクセルの明るさの違いのみを表現する方法です。これを行うことによって、画像のデータ量を減らし、処理にかかる時間を短縮することができます。</p><pre class="crayon-plain-tag">[In] :

#画像をグレースケール化して読み込み　※画像ファイル名の箇所は適宜変更してください。
original = cv2.imread('dog_image.jpg',0)</pre><p>
<h5>画像サイズ変更</h5>
<p>画像サイズの変更を行いましょう。アップロードした画像のサイズが大きいため、cv2.resizeを用いて画像サイズを横250ピクセル、縦180ピクセルに変更してみます。cv2.resizeの引数には(画像データ,(変更後の横、変更後の縦))の順に値を入力します。なお、matplotlibを用いると自動的にサイズを調整された画像が表示されてしまうため、ここでは先ほど定義した関数は用いずに、cv2_imshowを用います。</p><pre class="crayon-plain-tag">[In] :

#画像のサイズを変更
img0 = cv2.resize(original,(250,180))

#画像を表示
cv2_imshow(original)
cv2_imshow(img0)</pre><p>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/01/スクリーンショット-2021-01-08-14.51.42.png" /></p>
<p>画像サイズを小さくすることができました。なお、Google Colab以外で実装する場合には、画像が別のWindowで表示されるため、以下のコードが必要となります。本稿では、以後省略します。</p><pre class="crayon-plain-tag">[In] :

#Google Colab以外でOpenCVを用いて画像を表示する場合に必要なコード
cv2.waitKey(0) #キーボードが押されるまで待機
cv2.destroyAllWindows() #全てのウィンドウを閉じる</pre><p>
<h5>画像の色を変換</h5>
<p>次に、画像の色を変換します。<a href="https://www.codexa.net/matplotlib/">matplotlib</a>では画像の色データがRGBの順になっているものとして扱われるため、元の画像データの色がこの順でない場合、変換する必要があります。今回は、cv2.cvtCOLORを用いてBGRからRGBに変換します。これによって、matplotlibを用いた場合にも上の画像と同様にグレースケール化した画像が表示されます。</p><pre class="crayon-plain-tag">[In] :

#画像の色をmatplotlibに合わせて変換
img = cv2.cvtColor(img0, cv2.COLOR_BGR2RGB)

#画像を表示
show_img(img0,img)</pre><p>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/01/スクリーンショット-2021-01-08-14.51.56.png" /></p>
<h5>画像の平行移動</h5>
<p>次に、画像を平行移動させてみましょう。手順は以下のようになります。まず、画像の移動量を決めて変数に格納します。この際に、numpyのfloat32型のデータを2×3の行列に格納すること、下の例の0と1の値は変化させないことに注意してください。なお、以下の例は右に50ピクセル、下に30ピクセル移動させるように値を指定してありますが、これらの値を変化させることで上下左右に指定したピクセル分だけ移動させることができます。。次に、cv2.warpAffineを用いて画像を平行移動させます。cv2.warpAffineの引数には、（画像データ、2×3の行列で移動量を格納した変数、出力する画像のサイズ）を入力します。今回は、元の画像データのサイズと同じサイズにして出力してみましょう。</p><pre class="crayon-plain-tag">[In] :

#画像の移動量を決定して変数に格納
M = np.float32([[1,0,50],[0,1,30]])

#画像を平行移動
moved = cv2.warpAffine(img,M,(250,180))

#関数を利用して画像を表示
show_img(img,moved)</pre><p>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/01/スクリーンショット-2021-01-08-14.52.04.png" /></p>
<h4>画像の回転と拡大・縮小</h4>
<p>次は画像を回転させます。手順は先ほどの平行移動と似ていますが、今度は画像の行数・列数を直接入力せず、img.shapeから変数に格納して用いましょう。こうすることで、元の画像のサイズがわかっていない場合でも同様に回転させることが可能になります。では、具体的な手順を解説します。まず、画像の行数・列数をそれぞれ変数rowsとcolsに格納します。次に、cv2.getRotationMatrix2Dを用いて回転の仕方を示す行列を変数に格納します。cv2.getRotationMatrix2Dの引数には、（回転の中心、回転の角度、拡大・縮小の度合い）の順に入力します。今回は、回転の中心は画像の中心に、回転の角度は左に60度に、拡大・縮小の度合いは0.5に縮小するように指定してみましょう。回転の仕方を変数に格納した後は、先ほどの平行移動と同様に、cv2.warpAffineを用いて画像を処理して、関数を利用して表示します。</p><pre class="crayon-plain-tag">[In] :

#画像の行数・列数を変数に格納
rows,cols = img.shape[:2]

#画像の回転量を決定して変数に格納
M = cv2.getRotationMatrix2D((cols/2,rows/2),60,0.5)

#画像を回転
rotated = cv2.warpAffine(img,M,(cols,rows))

#関数を利用して画像を表示
show_img(img,rotated)</pre><p>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/01/スクリーンショット-2021-01-08-14.52.12.png" /></p>
<h4>画像のせん断</h4>
<p>さて、次は画像のせん断を行います。せん断とは、四角形の画像を平行四辺形に変形する処理で、元の画像中の３つの点と、その三点の処理後の位置をそれぞれ2×3の行列で指定することにより実行することができます。具体的には、処理前の画像中の３点の位置をpts1に、それら３点の処理後の位置をpts2にそれぞれ格納し、cv2.getAffineTransform(pts1,pts2)として変数に格納することで、せん断の処理を指定します。以下は、これまでの処理と同様に、cv2.warpAffineを用いて処理し、画像を表示します。以下の例をご覧ください。</p><pre class="crayon-plain-tag">[In] :

#画像の変換の度合いを決定して変数に格納
pts1 = np.float32([[40,40],[400,50],[10,220]])
pts2 = np.float32([[20,100],[400,50],[100,270]])
M = cv2.getAffineTransform(pts1,pts2)

#画像をせん断
affine = cv2.warpAffine(img,M,(cols,rows))

#関数を利用して画像を表示
show_img(img,affine)</pre><p>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/01/スクリーンショット-2021-01-08-14.52.22.png" /></p>
<h5>画像の平滑化</h5>
<p>次に解説するのが画像の平滑化です。簡単に言えば画像をぼかす処理です。この処理は非常にシンプルに行うことができ、cv2.blurの引数に画像データとぼかしに用いるフィルターのサイズを(横、縦)の順に指定するだけです。フィルターのサイズが大きければ大きいほど強いぼかしがかかり、小さければ小さいほどぼかしは弱くなります。</p><pre class="crayon-plain-tag">[In] :

#画像の平滑化
blurred = cv2.blur(img,(10,10))

#関数を利用して画像の表示
show_img(img,blurred)</pre><p>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/01/スクリーンショット-2021-01-08-14.52.30.png" /></p>
<h5>画像のしきい値処理</h5>
<p>しきい値処理も比較的シンプルに実装することができます。しきい値処理にはcv2.thresholdを用い、引数には（画像データ、しきい値、しきい値以上の値が変換される値、しきい値処理の手法）を指定します。以下の例で用いているBINARYという手法は単純にしきい値以上を白、しきい値以下を黒に変換する手法です。他にもBINARY_INVやTRUNCなどいくつもの設定が存在するので、ぜひ試してみてください。（参考：<a href="https://docs.opencv.org/master/db/d8e/tutorial_threshold.html">各設定の解説（英語）</a>）</p><pre class="crayon-plain-tag">[In] :

#しきい値処理
ret,binary = cv2.threshold(img,120,255,cv2.THRESH_BINARY)

#関数を利用して画像の表示
show_img(img,binary)</pre><p>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/01/スクリーンショット-2021-01-08-14.52.42.png" /></p>
<h5>画像のエッジ検出</h5>
<p>本稿で実装する最後の画像処理手法は画像のエッジ検出です。cv2.Cannyを用いてエッジ検出を行います。cv2.Cannyの引数には、（画像データ、下の閾値、上の閾値）を指定します。下の閾値と上の閾値の値を変化させることで、検出されるエッジの量が変化します。では、実際にエッジ検出をやってみましょう。</p><pre class="crayon-plain-tag">[In] :

#エッジ検出
edges = cv2.Canny(img,180,300)

#関数を利用して画像の表示
show_img(img,edges)</pre><p>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/01/スクリーンショット-2021-01-08-14.52.52.png" /></p>
<h4>画像内の物体検出（顔・目）</h4>
<p>次に実装する機能が画像内の物体検出です。本稿では、OpenCVにあらかじめ用意されている分類器を利用して人の顔の画像から顔と目を検出してみようと思います。まず、人の顔の画像をGoogle Colab上にアップロードした上で読み込みます。本稿では、「ぱくたそ」というサイトに掲載されているモデルのフリー画像を利用させていただきます。同じ画像を利用したい方は、<a href="https://www.pakutaso.com/userpolicy.html">「ぱくたそ」の利用規約</a>をご確認の上、下記URLからダウンロードしてください。なお、本稿の画像１は元画像の左上1280×853ピクセルを、画像２は元画像の中央上部1280×853ピクセルを「ぱくたそ」からダウンロードする際にトリミングしています。</p>
<p style="text-align: center;"><a href="https://www.pakutaso.com/20191101315post-24141.html">画像1URL</a>、<a href="https://www.pakutaso.com/20171130306post-13918.html">画像2URL</a></p>
<pre class="crayon-plain-tag">[In] :

#画像１・２を読み込み
face = cv2.imread('face_image.jpg')
face2 = cv2.imread('face_image2.jpg')</pre>
画像のサイズが大きすぎるため、cv2.resizeを用いて画像サイズを縮小しておきます。<br />
<pre class="crayon-plain-tag">[In] :

#画像１・２のサイズを変更
face = cv2.resize(face,(330,220))
face2 = cv2.resize(face2,(300,200))</pre>
次に、カスケード型分類器を読み込みます。カスケード型分類器とは、対象となる画像データの特徴を学習済みのモデルのことです。機械学習のアンサンブル学習に似た分類モデルで、複数の学習器から成ります。自分でカスケード型分類器を作ることも可能ですが、作成のためには多くの画像データを収集する必要があります。一方で、OpenCVには顔や目などのカスケード型分類器が用意されていて、手軽に利用することが可能です。本稿でも、OpenCVのカスケード型分類器を活用します。今回利用する分類器のファイル名は「haarcascade_frontalface_alt.xml」と「haarcascade_eye.xml」です。<a href="https://github.com/opencv/opencv/tree/master/data/haarcascades">こちらのサイト</a>から直接読み込むか、一度PCにダウンロードしてからColab上にアップロードして読み込みましょう。ここではColabにアップロードしたファイルを読み込むためのコードを記述しておきます。<br />
<pre class="crayon-plain-tag">[In] :

#カスケード型分類器を読み込み
face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_alt.xml')
eye_cascade = cv2.CascadeClassifier('haarcascade_eye.xml')</pre>
ようやく準備が整いました。いよいよ顔と目の検出を実装していきます。先ほど読み込んだカスケード型分類器のdetectMultiScaleというメソッドを用いて顔を検出します。正確には、顔と思われる範囲を検出し、その範囲の左上のx座標(x)、y座標(y)、顔の範囲の幅(w)、顔の範囲の高さ(h)を出力するので、これらをfacesという変数に格納します。detectMultiScaleの引数には、（画像データ、scaleFactor(デフォルト1.1)、minNeighbors(デフォルト３)）を指定します。scaleFactorは小さければ小さいほど顔の検出漏れが少なくなるものの、処理時間が長くなります。また、minNeighborsは小さければ小さいほど顔の検出漏れが少なくなりますが、誤検出が増えます。ちなみに、画像データ以外のこれらの引数は省略することもできます。<br />
<pre class="crayon-plain-tag">[In] :

#顔を検出
faces = face_cascade.detectMultiScale(face, 1.1, 3)</pre>
facesに格納された４つの値を用いて、顔を黒い線で囲みます。対象となる範囲を線で囲む際には、長方形を描画することができるcv2.rectangleを利用します。引数には、（画像データ、描画したい長方形の左上の点、描画したい長方形の右下の点、線の色、線の太さ）を順に指定します。そして、描画した顔の範囲を示す長方形の中で、カスケード型分類器を用いて目を検出し、顔と同様にその範囲を描画するようにしたのが以下のコードです。なお、下のコードでfor文による繰り返しが行われているのは、複数の顔と目が画像に含まれている場合にその全てを検出できるようにするためです。<br />
<pre class="crayon-plain-tag">[In] :

#検出した顔を黒い線で囲み、顔の範囲の中から目を検出して白い線で囲む
for (x,y,w,h) in faces:
    face = cv2.rectangle(face,(x,y),(x+w,y+h),(1,1,1),2)
    roi_color = face[y:y+h, x:x+w]
    eyes = eye_cascade.detectMultiScale(roi_color)

for (ex,ey,ew,eh) in eyes:
    cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(255,255,255),2)</pre>
では、顔と目の検出結果を表示してみましょう。<br />
<pre class="crayon-plain-tag">[In] :

#画像を表示
cv2_imshow(face)</pre>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/01/スクリーンショット-2021-01-08-14.53.06.png" /></p>
<p>顔と目を正しく検出できていますね。次に、もう一つの画像でも同様に顔と目を検出できるかどうか試してみましょう。</p><pre class="crayon-plain-tag">[In] :

#顔を検出して黒い線で囲み、目を検出して白い線で囲む
faces2 = face_cascade.detectMultiScale(face2, 1.1, 3)
for (x,y,w,h) in faces2:
    face2 = cv2.rectangle(face2,(x,y),(x+w,y+h),(1,1,1),2)
    roi_color = face2[y:y+h, x:x+w]
    eyes = eye_cascade.detectMultiScale(roi_color)

for (ex,ey,ew,eh) in eyes:
    cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(255,255,255),2)

#画像を表示
cv2_imshow(face2)</pre><p>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/01/スクリーンショット-2021-01-08-14.53.16.png" /></p>
<p>なんと、この画像では顔と目を検出することができませんでした。実際には画像に対象が写っているにも関わらず、検出がうまくいかない理由は様々ですが、先ほど学んだ画像処理の手法を活用して解決できることがあります。今回は、顔が傾いて写っているのが原因ではないかと推測できるため、画像を１５度左に回転させた上でもう一度顔と目の検出を行ってみたいと思います。</p><pre class="crayon-plain-tag">[In] :

#画像の行数・列数を変数に格納
rows,cols = img.shape[:2]

#画像の回転量を決定して変数に格納
M = cv2.getRotationMatrix2D((cols/2,rows/2),15,1)

#画像を回転
face2 = cv2.warpAffine(face2,M,(cols,rows))</pre><p>
&nbsp;</p><pre class="crayon-plain-tag">[In] :

#顔を検出して黒い線で囲み、目を検出して白い線で囲む
faces2 = face_cascade.detectMultiScale(face2, 1.1, 3)
for (x,y,w,h) in faces2:
    face2 = cv2.rectangle(face2,(x,y),(x+w,y+h),(1,1,1),2)
    roi_color = face2[y:y+h, x:x+w]
    eyes = eye_cascade.detectMultiScale(roi_color)

for (ex,ey,ew,eh) in eyes:
    cv2.rectangle(roi_color,(ex,ey),(ex+ew,ey+eh),(255,255,255),2)

#画像を表示
cv2_imshow(face2)</pre><p>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/01/スクリーンショット-2021-01-08-14.53.26.png" /></p>
<p>今度は問題なく顔と目を検出できました。なお、今回は顔が傾いているとうまく検出できませんでしたが、様々な傾きの顔を学習させた分類器を作成すれば対応可能かと思います。しかし、検出の際に画像を回転させる場合でも、対象の傾きにも対応した分類器を作成する場合でも、画像を回転させる手法を用いることになります。さらに、今回は画像の回転を例にあげましたが、当然他の画像処理の手法も検出精度の向上には不可欠です。この実装を通して、画像処理の手法は物体検出において非常に重要なことがお分かりいただけたのではないでしょうか。</p>
<h4>機械学習（K近傍法）による文字分類</h4>
<p>さて、最後にOpenCVを用いて機械学習による文字分類を行います。機械学習による文字分類はScikit-learnなどの機械学習ライブラリを用いても可能ですし、OpenCVよりも使い慣れたライブラリがある方も多いかと思います。しかし、画像処理および物体検出から機械学習までをまとめて実装できるのがOpenCVの特徴の一つですので、本稿ではあえてOpenCVで機械学習を行ってみたいと思います。</p>
<h5>データセットの準備とデータの確認</h5>
<p>今回機械学習に用いるデータはKaggleにて公開されている「Kuzushizi-MNIST」略して「KMNIST」というデータセットです。KMNISTは「日本古典籍くずし字データセット」を基に人文学オープンデータ共同利用センター（CODH）が作成したデータセットです。KMNISTデータセットはKaggleよりダウンロードすることが可能です。以下のURLから５つのファイルのダウンロードをお願いします。なお、データセットのダウンロードにはKaggleの無料アカウントが必要です。</p>
<p>【データ取得元】</p>
<p><a href="https://www.kaggle.com/anokas/kuzushiji/">Kuzushiji-MNIST | Kaggle</a></p>
<p>【ダウンロードデータファイル名】</p>
<ul>
<li>kmnist_classmap.csv</li>
<li>kmnist-test-imgs.npz</li>
<li>kmnist-test-labels.npz</li>
<li>kmnist-train-imgs.npz</li>
<li>kmnist-train-labels.npz</li>
</ul>
<p>【データセット クレジット】<br />
『KMNISTデータセット』（CODH作成） 『日本古典籍くずし字データセット』（国文研ほか所蔵）を翻案 doi:10.20676/00000341</p>
<p>画像データの2つはZIP形式で圧縮されています。お手持ちの解凍ソフトでローカルに解凍してください。</p>
<p>ダウンロードしたデータセットを利用するため、Google Colabを利用している方は実装を行うファイル上にアップロードしてください。また、ローカル環境で実装する方は、ダウンロードしたファイルを適切なフォルダに配置してください。そうしたら、KMNISTのデータを変数に格納しましょう。ダウンロードしたデータは数字と文字の対応表、特徴量とラベルそれぞれの訓練データとテストデータですので、以下のように適切な変数名をつけて利用します。</p><pre class="crayon-plain-tag">[In] :

#KMINSTのデータを変数に格納
classes = pd.read_csv('kmnist_classmap.csv')
X_train = np.load('kmnist-train-imgs.npz')["arr_0"]
y_train = np.load('kmnist-train-labels.npz')["arr_0"]
X_test = np.load('kmnist-test-imgs.npz')["arr_0"]
y_test = np.load('kmnist-test-labels.npz')["arr_0"]</pre><p>
はじめに、classesを開いてみましょう。classesには数字と文字の対応表が格納されています。このデータセットに含まれているくずし字は、以下の10種類のようです。この対応表を用いることで数値データとくずし字を相互に変換することができます。</p><pre class="crayon-plain-tag">[In] :

#数字と文字の対応表を表示
classes</pre><p>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/01/スクリーンショット-2021-01-08-14.53.38.png" /></p>
<p>次に、特徴量とラベルそれぞれの訓練データとテストデータのサイズを確認してみましょう。</p><pre class="crayon-plain-tag">[In] :

#データのサイズを確認
print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)</pre><p>
</p><pre class="crayon-plain-tag">[Out] :

(60000, 28, 28)
(10000, 28, 28)
(60000,)
(10000,)</pre><p>
特徴量すなわちくずし字の画像データは、28ピクセル×28ピクセルのデータで、60000の訓練データと10000のテストデータに分割されていることがわかります。また、特徴量の分割に対応して、ラベルも60000の訓練データと10000のテストデータに分割されています。</p>
<p>次に、くずし字を表示してみましょう。2021年ということで、訓練データの2021番目に格納されているくずし字を表示してみます。なお、plt.cm.grayを用いて白黒で表示します。</p><pre class="crayon-plain-tag">[In] :

#画像を表示
plt.imshow(X_train[2021], cmap = plt.cm.gray)
plt.show()</pre><p>
<img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2021/01/スクリーンショット-2021-01-08-14.54.50.png" /><br />
さすがくずし字といったところでしょうか。先ほどの対応表に記載されている10種類のひらがなのどれに該当するのかわかりませんね。。。ということで、ラベルと先ほどの対応表からこの文字がどのひらがななのかを確認してみましょう。</p><pre class="crayon-plain-tag">[In] :

print(y_train[2021])
print(classes)</pre><p>
</p><pre class="crayon-plain-tag">[Out] :

8
   index codepoint char
0      0    U+304A    お
1      1    U+304D    き
2      2    U+3059    す
3      3    U+3064    つ
4      4    U+306A    な
5      5    U+306F    は
6      6    U+307E    ま
7      7    U+3084    や
8      8    U+308C    れ
9      9    U+3092    を</pre><p>
ラベルの訓練データの２０２１番目には８が格納されており、対応表と突き合わせると先ほどの文字が「れ」であることがわかりました。</p>
<h5>データの前処理</h5>
<p>ここまでで、KMNISTデータセットについては概ね理解していただけたかと思います。ここからは機械学習の準備を進めていきましょう。ここでの工程は以下の三つです。</p>
<ol>
<li> 画像データを１行に変換</li>
<li> データの型を変換</li>
<li> 画像データの正規化</li>
</ol>
<p>まず、画像データを２８行×２８列のデータから１行×７８４列のデータに変形します。</p><pre class="crayon-plain-tag">[In] :

#画像データを二次元から一次元に変換
X_train = X_train.reshape(60000,784)
X_test = X_test.reshape(10000,784)

#データのサイズを再度確認
print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)</pre><p>
</p><pre class="crayon-plain-tag">[Out] :

(60000, 784)
(10000, 784)
(60000,)
(10000,)</pre><p>
次に、各データの型を変更します。元のデータ型だとエラーが起こりますが、特徴量（画像データ）をnp.float32に、ラベルをnp.int64にすることで問題なく学習を進めることができます。</p><pre class="crayon-plain-tag">[In] :

#各データのデータ型の変更
X_train = X_train.astype(np.float32)
X_test = X_test.astype(np.float32)
y_train = y_train.astype(np.int64)
y_test = y_test.astype(np.int64)

#各データのデータ型の確認
print(X_train.dtype)
print(X_test.dtype)
print(y_train.dtype)
print(y_test.dtype)</pre><p>
</p><pre class="crayon-plain-tag">[Out] :

float32
float32
int64
int64</pre><p>
最後に、画像データの正規化を行います。正規化とは、データの値を０〜１の範囲に収まるように変換することです。正規化を行うことによって、より効率的に学習を行えるようになります。本稿で用いている画像データは、各ピクセルの色が０〜２５５のいずれかで表現されているので、全てのピクセルの値を２５５で割ることによって、最小値が０、最大値が１のデータセットにすることができます。正規化を行なった後で、試しにデータの一部を表示してみます。</p><pre class="crayon-plain-tag">[In] :

#画像データの正規化
X_train = X_train / 255
X_test = X_test / 255
print(X_train[1,150:170])</pre><p>
</p><pre class="crayon-plain-tag">[Out] :

[0.03921569 0.30588236 0.99215686 1.         1.         0.6509804
 0.01568628 0.         0.03529412 0.         0.         0.
 0.         0.         0.         0.         0.         0.
 0.         0.        ]</pre><p>
訓練データの１枚目の画像から、１５１〜１７１番目のピクセルの値を表示しました。０〜１の値に収まっていることが見て取れますね。</p>
<h5>k近傍法による学習と予測の実装</h5>
<p>機械学習に入る前に、本稿で用いる手法「k近傍法」の概要を説明します。k近傍法は、単純に言うと「新しいサンプルと特徴が似ているいくつかのサンプルのラベルを参考にして、新しいサンプルのラベルを予測する分類手法」です。より具体的に言うならば、k近傍法では特徴量から各サンプルの空間上の位置を算出し、各サンプルを空間上にプロットします。そして、新しい点に対して各サンプルとの距離を計算し、距離が近い複数の（k個の）点のラベルから新しい点のラベルを予測します。下のモデル図でイメージを掴みましょう。</p>
<p><img class="aligncenter" src="https://www.codexa.net/wp-content/uploads/2020/12/スクリーンショット-2020-12-24-16.08.03.png" /></p>
<p>青い四角形と黄色い三角形をそれぞれの特徴から図のように二次元空間にプロットしたとします。そこで、新しいサンプル（赤い四角）が四角形であるのか三角形であるのかをK近傍法で予測します。仮にk=5、すなわち距離が近い５つのサンプルのラベルを参考にする場合、５つのサンプルのラベルは（四角、三角、四角、四角、四角）となるため、新しいサンプルは四角形だと予測することができます。上の図は厳密なものではありませんが、このようなイメージで理解していただくと良いかと思います。</p>
<p>それでは、実際にモデルにデータを学習させ、予測を行います。まず、OpenCVのcv2.ml.KNearst_createを用いてk近傍法のインスタンスを作成します。</p><pre class="crayon-plain-tag">[In] :

#k近傍法のインスタンスを作成
knn = cv2.ml.KNearest_create()</pre><p>
モデルによるデータの学習を行います。knn.trainの引数に（特徴量の訓練データ、cv2.ml.ROW_SAMPLE、ラベルの訓練データ）を指定します。cv2.ml.ROW_SAMPLEは、訓練データがそれぞれ１行のデータであることを示す引数です。</p><pre class="crayon-plain-tag">[In] :

#モデルによる学習
knn.train(X_train,cv2.ml.ROW_SAMPLE,y_train)</pre><p>
</p><pre class="crayon-plain-tag">[Out] :

True</pre><p>
knn.findNearestで学習したモデルによる予測を行います。引数には、特徴量のテストデータと予測の際に参照する近距離の値の数を指定します。このk近傍法のモデルは４種類の値を出力します。本稿では距離が近い５つの文字を予測に用いるように指定したため、return valueと予測値に加えて、各画像の文字と距離が近かった文字上位５つ、それら５つの文字までの距離の４種類です。後者二つはknn.findNearestの第二引数の値によって変化します。k近傍法は新たな点に対して全ての画像との距離を算出する手法であるため、計算量が非常に多く、実行環境によっては多少時間がかかる可能性がある点にはご留意ください。ちなみにGoogle Colab上で実行した場合には、処理に数分程度時間がかかりました。</p><pre class="crayon-plain-tag">[In] :

#学習したモデルによる予測
ret,predicted,neighbors,dist = knn.findNearest(X_test,k=5)</pre><p>
予測ができたら、正解ラベル、予想値、予想の正解率を表示します。なお、予想の正解率はラベルのテストデータと予想値の一致数をテストデータのサンプル数10000で割って求めています。</p><pre class="crayon-plain-tag">[In] :

#正解ラベル、予想値、正解率を表示
print("y_test:", y_test)
print("predicted:", predicted.T)
score = np.sum(y_test == predicted.T)/10000
print("Score:", score)</pre><p>
</p><pre class="crayon-plain-tag">[Out] :

y_test: [2 9 3 ... 9 4 2]
predicted: [[2. 9. 3. ... 9. 4. 2.]]
Score: 0.9066</pre><p>
シンプルなk近傍法での予測でしたが、90.66％とまずまずの正解率を出すことができていますね。<br />
最後に、予測の際に出力された距離の近さ上位５つの文字とそれら５つの文字までの距離も表示してみましょう。</p><pre class="crayon-plain-tag">[In] :

#予測の詳細（距離の近さ上位５つの文字、５つの文字までの距離）を表示
print(neighbors)
print(dist)</pre><p>
</p><pre class="crayon-plain-tag">[Out] :

[[2. 2. 2. 2. 2.]
 [9. 9. 9. 9. 9.]
 [3. 3. 3. 3. 3.]
 ...
 [9. 9. 9. 9. 9.]
 [4. 4. 4. 4. 4.]
 [2. 2. 2. 8. 2.]]
[[15.663882 17.158724 17.50505  18.134102 18.247566]
 [30.09421  37.115906 42.739445 45.71484  47.19342 ]
 [30.3142   35.68461  39.314682 44.805183 46.276993]
 ...
 [71.988785 78.130745 78.78104  82.05212  84.749115]
 [57.134342 68.31008  69.406296 72.67645  73.22733 ]
 [28.465158 39.65799  40.94851  42.43521  43.218506]]</pre><p>
まず、距離が近い上位五つの文字に関しては概ね予想通りに予測されています。しかし、最後の行を見ると、４つ目の値が８になっています。先ほどの対応表を確認すると２は「す」で８は「れ」なので、「す」という文字との距離の近さの順位で４番目に「れ」が入ってしまったということになります。また、これら五つの文字への距離は下の行列で示されていますが、画像によってもっとも近い文字への距離にもばらつきがあることがわかります。</p>
<h3>まとめ</h3>
<p>いかがでしたか？本稿ではOpenCVの機能についての解説や他のライブラリとの比較、３つの実装を行いました。かなり盛りだくさんの内容でしたが、OpenCVについてかなり理解を深めていただけたのではないかと思います。</p>
<p>codexaでは、OpenCVを用いた基本的な画像処理から畳み込みニューラルネットワークの活用までまとめた画像認識のコース「<a href="https://www.codexa.net/keras-cnn-python/">はじめての画像認識</a>」もご用意しています。低価格でご利用いただけますので、ぜひこちらも活用して画像認識についてさらなる知識とスキルを身につけてください。</p>
<p>最後までお読みいただきありがとうございました！</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.codexa.net/opencv_python_introduction/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Python 機械学習ライブラリを23個一挙紹介！データ処理から深層学習まで完全網羅</title>
		<link>https://www.codexa.net/machine-learning-python-library/</link>
					<comments>https://www.codexa.net/machine-learning-python-library/#respond</comments>
		
		<dc:creator><![CDATA[codexaチーム]]></dc:creator>
		<pubDate>Wed, 18 Nov 2020 08:30:20 +0000</pubDate>
				<category><![CDATA[機械学習]]></category>
		<guid isPermaLink="false">https://www.codexa.net/?p=2769</guid>

					<description><![CDATA[Pythonで知っておくべき機械学習ライブラリを徹底解説！各機械学習ライブラリの特徴や概要、モデル構築の流れと併せて、機械学習ライブラリについての確かな知識を身につけましょう。]]></description>
										<content:encoded><![CDATA[<p>機械学習モデルを構築する際に欠かせないのが、機械学習ライブラリです。様々な機械学習ライブラリがインターネット上で提供されていることが、昨今の機械学習ブームを支えているのは間違いありません。</p>
<p>しかし、一言で機械学習ライブラリといっても、その用途は様々です。どのライブラリを、機械学習モデル構築の流れの中のどの工程で用いればいいのかがわかっていなければ、スムーズにモデル構築を進めることはできません。</p>
<p>そこで本稿では、機械学習ライブラリとは何なのか、それぞれのライブラリがどのような特徴を持つのかをまとめました。機械学習モデル構築の流れと併せて、機械学習ライブラリについての確かな知識を身につけましょう。</p>
<h3>機械学習ライブラリの定義と実装の流れ</h3>
<p>まずは、機械学習ライブラリの定義について確認し、機械学習モデル実装の流れを概説します。これによって、次節で紹介するライブラリを、それぞれどのような場面で利用すればいいのかイメージしやすくなるでしょう。</p>
<h4>ライブラリとは</h4>
<p>ライブラリとは、「特定の処理を実行するためのプログラムをまとめたもの」です。例えるならば、システム構築のための道具箱のようなものでしょうか。他人が作成したプログラムも利用することができるため、大幅な作業時間の短縮になります。また、目的に応じて適切なライブラリから適切なプログラムを呼び出して用いることで、実行できる処理の幅を飛躍的に広げることができます。</p>
<p>なお、ライブラリと似た用語にフレームワークがありますが、厳密にはこれらは異なります。フレームワークは「全体が大まかに設計されたプログラムの集合体」のようなものです。ライブラリが道具箱ならば、フレームワークは雛形です。フレームワークを活用しつつ、細部を目的に合わせてカスタマイズすることで、独自の処理を実行することができます。</p>
<h4>機械学習ライブラリとは</h4>
<p>前述のライブラリの説明に則ると、機械学習ライブラリとは、「機械学習モデルを構築するためのプログラムをまとめたもの」です。なお、本稿では、機械学習を実際に構築する際に用いるライブラリを「狭義の機械学習ライブラリ」とし、実際のモデル構築には用いないものの、機械学習モデル開発の流れの中で利用するライブラリを「広義の機械学習ライブラリ」として扱います。言い換えると、狭義の機械学習ライブラリは機械学習のモデリング機能を持つライブラリを、広義の機械学習ライブラリは機械学習モデル構築に関わる全てのライブラリを指します。</p>
<h4>機械学習モデル構築の流れ</h4>
<p>機械学習ライブラリを紹介する前に、機械学習モデル構築の流れを概観しておきます。機械学習モデルは、一般に以下のような流れで構築します。</p>
<p><img src="https://www.codexa.net/wp-content/uploads/2020/11/スクリーンショット-2020-11-12-16.38.16.png" /></p>
<p>1. データの準備・確認</p>
<p>コンピュータに学習させるデータを準備し、コンピュータに読み込ませて、学習に使えるようにデータを整形します。例えば、複数のデータセットを一つのデータセットにまとめたり、適切な変数名をつけなおしたりします。</p>
<p>2. データの可視化・分析</p>
<p>次に、データを２次元や３次元の様々な図やグラフを用いて表現し、その特徴を分析します。これにより、異常なデータが含まれていないかどうかを確認したり、重要な変数を見つけだしてモデル構築のヒントを得たりすることができます。（参照：<a href="https://www.codexa.net/basic-exploratory-data-analysis-with-python/">EDA 探索的データ解析とは？</a>）</p>
<p>3. データの前処理</p>
<p>機械学習モデルの構築に入る前に、データの前処理を行います。この工程により、学習が正しく実行されるようにするとともに、モデルによる予測の精度を高めることができる場合があります。具体的には、欠損値を何らかの値で埋めたり、データを標準化したりします。</p>
<p>4. モデルの構築</p>
<p>機械学習モデルを構築します。データの量や種類などの特徴に合わせて、適切なライブラリやフレームワークを選択してモデルを構築し、コンピュータによる学習を行います。</p>
<p>5. モデルの評価・選択</p>
<p>データを学習させたモデルがどれくらいの精度で予測可能なのか、評価指標を用いて確認します。評価指標の値を分析し、より良いと思われるモデルを構築して予測・評価を繰り返し、最も性能がいいモデルを選択します。（参考：<a href="https://www.codexa.net/ml-evaluation-cls/">機械学習の評価指標 分類編</a>）</p>
<h3>主な機械学習ライブラリ</h3>
<p>機械学習モデル構築の流れを把握できたところで、主な機械学習ライブラリについて解説していきます。まずは、広義の機械学習ライブラリから見ていきましょう。</p>
<h4>Numpy</h4>
<div style="width: 132px" class="wp-caption alignright"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2020/11/スクリーンショット-2020-11-05-12.48.11.png" alt="" width="122" height="126" /><p class="wp-caption-text">＜引用：<a href="https://numpy.org/">numpy公式</a> ＞</p></div>
<p>Numpy（ナンパイ）はPython用の数値計算ライブラリです。コアとなる部分がコンパイル型の言語であるC言語で書かれているため、インタプリタ型のPython自体よりも早く演算処理を行うことができます。また、大規模な多次元配列や行列の計算を行うことができ、Pythonにおける数値計算に関してはデファクトスタンダードになっていると言っても過言ではないでしょう。公式ウェブサイトに掲載されているnumpyのケーススタディには、クリケットの分析や、アインシュタインが存在を予測した重力波の検出についての記事があります。多様な分野で認められ、活用されているライブラリである証拠ですね。機械学習モデル構築においては、大規模なデータを演算する際に活用することができます。</p>
<p>&gt; <a href="https://numpy.org/">公式サイト</a><br />
&gt; <a href="https://www.codexa.net/numpy/">NumPy 入門無料コース</a></p>
<h4>pandas</h4>
<div style="width: 285px" class="wp-caption alignright"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2020/11/スクリーンショット-2020-11-05-12.47.55.png" alt="" width="275" height="97" /><p class="wp-caption-text">＜引用：<a href="https://pandas.pydata.org/">pandas公式</a> ＞</p></div>
<p>pandas(パンダス)はPython上で動くデータ処理・分析用ライブラリです。最大の特徴は、データフレームという行列形式のデータを処理するための様々な方法を備えていることです。データの結合や変形、特定の行や列の抜き出しや挿入、特定の行や列の値に基づくグループ化、さらにはCSV・テキスト・エクセルなどのデータの読み書きと、データセットに関して極めて幅広い処理が可能です。ゆえに、pandasを使いこなせれば、収集された生データを機械学習モデルで分析可能な状態に整形することができます。</p>
<p>&gt; <a href="https://pandas.pydata.org/">公式サイト</a><br />
&gt; <a href="https://www.codexa.net/pandas/">pandas 入門無料コース</a><br />
&gt; <a href="https://www.amazon.co.jp/dp/4798160679/">現場で使える! pandasデータ前処理入門</a>（入門書籍 / Amazon）</p>
<h4>matplotlib</h4>
<div style="width: 530px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2020/11/スクリーンショット-2020-11-05-13.02.13.png" alt="" width="520" height="124" /><p class="wp-caption-text">＜引用：<a href="https://matplotlib.org/">matplotlib公式</a> ＞</p></div>
<p>&nbsp;</p>
<p>次に、matplotlib（マットプロットリブ）を紹介します。matplotlibはMATLABというデータ解析用の言語を模倣して作られた、Python用のデータ可視化ライブラリです。主にPythonで書かれていますが、前述のnumpyを始め他のライブラリも活用することで処理性能を向上させ、大規模なデータの可視化に対応しています。<a href="https://matplotlib.org/gallery/index.html">公式サイトのギャラリー</a>にあるように、matplotlibを使うことで、データを非常に多様な方法で可視化することができます。機械学習モデルの構築においては、可視化を通して、数値だけではわかりづらいデータの分布やデータ同士の連関についての理解を深めます。そうすることで、不必要なデータを除外したり、モデル構築の方針を立てたりすることができるのです。</p>
<p>&gt; <a href="https://matplotlib.org/">公式サイト</a><br />
&gt; <a href="https://www.codexa.net/matplotlib/">matplotlib 入門無料コース</a></p>
<h4>seaborn</h4>
<div style="width: 364px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2020/11/スクリーンショット-2020-11-05-12.46.13.png" alt="" width="354" height="105" /><p class="wp-caption-text">＜引用：<a href="https://seaborn.pydata.org/">seaborn公式</a> ＞</p></div>
<p>&nbsp;</p>
<p>matplotlibと並んで有名かつ便利な可視化ライブラリにseaborn（シーボーン）があります。seabornの主な特徴については、<a href="https://www.codexa.net/seaborn-python/">seabornについての記事</a>でもご紹介したように、以下の点が挙げられます。１点目は、matplotilbと比べて洗練された図を描くことができることで、２点目は、matplotlibと比べて少ないコードで可視化が可能であることです。matplotlibの方が一般に広く活用されてはいるものの、seabornも可視化には非常に便利なライブラリとなっています。</p>
<p>&gt; <a href="https://seaborn.pydata.org/">公式サイト</a></p>
<h4>SciPy</h4>
<div style="width: 99px" class="wp-caption alignright"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2020/11/スクリーンショット-2020-11-05-13.06.19.png" alt="" width="89" height="84" /><p class="wp-caption-text">＜引用：<a href="https://www.scipy.org/">SciPy公式</a> ＞</p></div>
<p>&nbsp;</p>
<p>SciPy（サイパイ）は、数値積分や最適化、線形代数や統計的な手法による計算などの複雑な数値処理を行うためのライブラリです。機械学習モデル構築においては、データの前処理などに活用することができます。</p>
<p>&gt; <a href="https://www.scipy.org/scipylib/index.html">公式サイト</a></p>
<p>&nbsp;</p>
<p>ここからは、機械学習ライブラリの中でも、実際に機械学習モデルを構築する際に用いる狭義の機械学習ライブラリや機械学習フレームワークについて解説します。</p>
<h4>scikit-learn</h4>
<div style="width: 479px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2020/11/スクリーンショット-2020-11-05-13.13.44.png" alt="" width="469" height="255" /><p class="wp-caption-text">＜引用：<a href="https://scikit-learn.org/stable/">scikit-learn公式</a> ＞</p></div>
<p>まずは、機械学習ライブラリの定番であるscikit-learn（サイキット・ラーン）についてご説明しましょう。scikit-learnは前述のNumpy, SciPy, matplotlib上で動作する、オーソドックスな機械学習ライブラリです。scikit-learnには、大きく分けて「回帰」、「分類」、「クラスタリング」、「次元削減」、「データの前処理」、「モデルの評価と選択」という6つの機能があり、前処理以降のモデル構築プロセスを広範にカバーしているのが最大の特徴です。サンプルのデータセットや公式の丁寧なガイドも用意されているので、手軽に機械学習を試すこともできます。</p>
<p>&gt; <a href="https://scikit-learn.org/stable/">公式サイト</a><br />
&gt; <a href="https://www.codexa.net/scikit-learn-intro/">scikit-learn 入門：6つの機能と分類・回帰の実装方法を徹底解説！</a></p>
<h4>XGBoost・LightGBM</h4>
<p>XGBoost（エックスジーブースト）とLightGBM（ライトジービーエム）は、どちらも勾配ブースティングによる機械学習のフレームワークです。勾配ブースティングとは、決定木アルゴリズムに基づいた機械学習手法で、Kaggleのコンペティションでも非常に人気があります。scikit-learnのように前処理まで対応しているわけではありませんが、高精度の機械学習モデルを構築するという観点から見て非常に優れたフレームワークです。また、どちらも、PythonだけでなくCやRなどの複数のプログラミング言語に対応しています。scikit-learnでのモデル構築に習熟してきたなと感じたら、これらのフレームワークを活用してみると良いでしょう。</p>
<p>&gt; <a href="https://xgboost.readthedocs.io/en/latest/">XGBoost 公式サイト</a><br />
&gt; <a href="https://www.codexa.net/xgboost-tutorial/">XGBoost 徹底入門コース</a></p>
<p>&gt; <a href="https://lightgbm.readthedocs.io/en/latest/">LightGBM 公式サイト</a><br />
&gt; <a href="https://www.codexa.net/lightgbm-beginner/">LightGBM 徹底入門</a></p>
<h3>深層学習（ディープラーニング）のフレームワーク</h3>
<p>さて、機械学習ライブラリについて一通り学んだので、本節では機械学習の中でも特に注目を集めている深層学習のフレームワークを見ていきましょう。「そもそも深層学習って何？」と疑問をお持ちの方は、<a href="https://www.codexa.net/what-is-deep-learning/">こちら</a>を参考にしてください。</p>
<h4>TensorFlow</h4>
<div style="width: 397px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2020/11/スクリーンショット-2020-11-05-15.51.19.png" alt="" width="387" height="97" /><p class="wp-caption-text">＜引用：<a href="https://www.tensorflow.org/?hl=ja">TensorFlow公式</a> ＞</p></div>
<p>&nbsp;</p>
<p>まずは、Googleが提供している機械学習ライブラリのTensorFlow（テンソルフロー）から解説します。TensorFlowは機械学習ライブラリですが、深層学習（ディープラーニング）にまで対応しているのが特徴です。また、PCでPythonを用いて利用するだけでなく、JavaScriptやモバイルデバイス、クラウド上での利用にも対応したバージョンがあり、様々な場面で利用することができます。さらに、TensorBoardという可視化ツールが付属しているため、図を用いて直感的に理解しつつ、モデル構築を進めることができます。</p>
<p>&gt; <a href="https://www.tensorflow.org/?hl=ja">公式サイト</a><br />
&gt; <a href="https://www.codexa.net/tensorflow-for-begginer/">TensorFlow 徹底入門</a></p>
<h4>Keras</h4>
<p>Keras（ケラス）は、TensorFlowをはじめとしていくつかの機械学習ライブラリ上で動かすことができる深層学習ライブラリです。特徴としては、いくつものライブラリ上で動かせる点に加えて、シンプルで簡単に深層学習モデルを構築できる点や、分散処理に対応していることで効率的な学習ができる点などが挙げられます。</p>
<p>&gt; <a href="https://keras.io/ja/">公式サイト</a><br />
&gt; <a href="https://www.codexa.net/cnn-mnist-keras-beginner/">KerasによるCNN</a><br />
&gt; <a href="https://www.codexa.net/keras-lstm-cryptos-forecast/">Kerasによる時系列モデル</a></p>
<h4>PyTorch</h4>
<div style="width: 283px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2020/11/スクリーンショット-2020-11-05-16.47.12.png" alt="" width="273" height="79" /><p class="wp-caption-text">＜引用：<a href="https://pytorch.org/">PyTorch公式</a> ＞</p></div>
<p>Facebookの人工知能研究グループが開発したPython用の機械学習ライブラリが、Pytorch（パイトーチ）です。セールスフォース・ドットコムやスタンフォード大学での開発に利用されています。また、後述のChainer（チェイナー）を開発した日本の有力なベンチャー企業「Prefferd Networks」が、研究開発基盤を自社のChainerからPyTorchに移行したことからも、PyTorchの競争力の高さが窺えます。そんなPytorchの特徴として挙げられるのが、操作方法がNumpyに類似している点です。日頃、numpyを扱うPythonエンジニアであれば容易に扱えるのは嬉しいですね。また、ニューラルネットワークの構築の際に必要となる「計算グラフ」が動的に構築される点（Define-by-Run方式）で、TensorFlowやKerasと異なります。さらに、分散処理への対応によるパフォーマンスの最適化や、ツールやライブラリといった開発エコシステムの充実、主要なクラウドプラットフォームにも対応している点なども特徴といえます。</p>
<p>&gt; <a href="https://pytorch.org/">公式サイト</a><br />
&gt; <a href="https://www.codexa.net/pytorch-python/">PyTorch入門</a></p>
<h4>Chainer</h4>
<div style="width: 340px" class="wp-caption aligncenter"><img loading="lazy" src="https://www.codexa.net/wp-content/uploads/2020/11/スクリーンショット-2020-11-12-16.52.20.png" alt="" width="330" height="81" /><p class="wp-caption-text">＜引用：<a href="https://chainer.org/">Chainer公式</a> ＞</p></div>
<p>Chainer（チェイナー）は、Prefferd Networksによって開発され、2015年にオープンソースとして公開されたディープラーニングフレームワークです。2019年に、Prefferd Networksの開発基盤のPytorchへの移行に伴い、Chainerは新たな開発を停止してメンテナンスフェーズへと移行しました。しかし、日本企業が開発したフレームワークであるため、日本語で得られる情報量も多く、特に日本では他の深層学習フレームワークと並んで人気があります。PyTorchにも採用されているDefine-by-Run方式をはじめに採用した点が特徴です。</p>
<p>&gt; <a href="https://chainer.org/">公式サイト</a></p>
<h3>データの特徴別機械学習ライブラリ（自然言語、画像、音声）</h3>
<p>さて、この節では、データの特徴に応じて用いる専門性の高いライブラリについて解説します。自然言語データ、画像データ、音声データはそれぞれ特有の性質があるため、独自の分野が形成されています。ライブラリに関しても、それぞれのデータに特化したものをまとめてご紹介していきます。</p>
<h4>自然言語処理</h4>
<p>まずは自然言語処理です。自然言語とは人間が用いる言語のことで、コンピュータが処理する機械語に対応した表現です。日本語も英語も自然言語の一つです。自然言語処理とは、そんな自然言語を単語単位に分割して、数学的・統計的理論に基づいて解析する技術です。自然言語処理は、Googleなどの検索エンジンやAppleのSiriやAmazonのAlexaといったスマートアシスタント、メールのスパムフィルター、テキストの予測変換、機械翻訳など、様々な分野に活用されています。自然言語処理ライブラリの例としては、以下のものが挙げられます。</p>
<ul>
<li><a href="https://taku910.github.io/mecab/">Mecab</a>：オープンソースの形態素解析エンジン。複数のプログラミング言語に対応しており、辞書の追加も容易。（参考：<a href="https://www.codexa.net/mecab-python/">Mecab入門</a>）</li>
<li><a href="https://mocobeta.github.io/janome/">Janome</a>：Pythonで書かれた形態素解析器。辞書も内包されていて利用しやすいのが特徴。</li>
<li><a href="https://chasen-legacy.osdn.jp/">Chasen</a>：奈良先端科学技術大学院大学松本研究室で開発された形態素解析器。</li>
<li><a href="http://nlp.ist.i.kyoto-u.ac.jp/index.php?JUMAN">JUMAN</a>：京都大学大学院の黒橋・褚・村脇研究室で開発された日本語形態素解析システム。</li>
<li><a href="https://megagonlabs.github.io/ginza/">GINZA</a>：オープンソース日本語自然言語処理ライブラリ。</li>
</ul>
<h4>画像認識</h4>
<p>画像認識とは、コンピュータに大量の画像を読み込ませて学習させることにより、物体の識別を可能にする技術です。自然言語処理技術同様、スマートフォンの顔認証やカメラの顔認識機能、医療現場での病変の検出や自動運転車といったように様々な分野で活用されています。画像認識のためのライブラリには以下のようなものがあります。</p>
<ul>
<li><a href="https://opencv.org/">OpenCV</a>：Open Source Computer Vision Libraryの略称。複数のプログラミング言語とOSに対応。</li>
<li><a href="https://pillow.readthedocs.io/en/stable/">pillow</a>：Python Imaging Library(PIL)を元にした画像処理ライブラリ。</li>
<li><a href="https://scikit-image.org/">scikit-image（サイキットイメージ）</a>：画像処理に特化したPythonライブラリ。</li>
</ul>
<h4>音声認識</h4>
<p>音声認識とは、デジタル化した音声をコンピュータで学習することで、音声を判別したり、新たな音声を生成するための技術です。活用例としては、SiriやGoogle Home、音声による検索機能、会議の議事録・コールセンターでの問い合わせの文字起こしなどが挙げられます。</p>
<ul>
<li><a href="https://pypi.org/project/SpeechRecognition/">speech recognition</a>：様々な音声認識エンジン、音声認識APIをサポートする音声認識ライブラリ。</li>
<li><a href="https://people.csail.mit.edu/hubert/pyaudio/docs/">pyaudio（パイオーディオ）</a>：PortAudioをPythonで利用可能にするライブラリ。</li>
<li><a href="https://julius.osdn.jp/">Julius</a>：音声認識システムの開発・研究のための、オープンソース汎用大語彙連続音声認識エンジン</li>
</ul>
<h3>まとめ</h3>
<p>いかがでしたでしょうか。本稿を通して、どの機械学習ライブラリをどの場面で使うのか、マクロなイメージを掴むことが出来ていたら幸いです。機械学習モデル構築の全体像を知ったところで、各種ライブラリを活用して実際に機械学習モデルを構築してみることをお勧めします。</p>
<p>codexaでは、本文中で参考としてご紹介した記事の他にも機械学習ライブラリの講座を提供しています。どれも無料あるいは数百円で受講可能なので、ぜひこの機会にご利用ください。</p>
<ul>
<li><a href="https://www.codexa.net/numpy/">Numpy入門</a></li>
<li><a href="https://www.codexa.net/pandas/">Pandas入門</a></li>
<li><a href="https://www.codexa.net/matplotlib/">Matplotlib入門</a></li>
<li><a href="https://www.codexa.net/xgboost-tutorial/">XGBoost</a></li>
<li><a href="https://www.codexa.net/decision-tree-random-forest/">決定木とランダムフォレスト</a>（Scikit-learn利用）</li>
<li><a href="https://www.codexa.net/keras-cnn-python/">はじめての画像認識</a>（OpenCV、Keras利用）</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://www.codexa.net/machine-learning-python-library/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
