2019年10月アーカイブ

はじめに

最近天気が不安定で、急な雨が多くまいっています。(しかも冬の雨はツライ。)
ついつい出かける時に傘を持たずに、夜、雨に降られることも多いですよね。
現在、Pythonを使った機械学習を勉強しているので、kaggleだけではなく、実際にテーマを決めて何か簡単な実装をしてみたいと思い、今回は気象庁のデータを使って ある日に雨が降っている or いないという2値の予測を実装をします。
自分で作ると愛着が湧いて、ちょくちょく予測して傘を忘れずに済むだろう。そして濡れることはもうないだろう!をモチベーションに頑張ります。


目次


気象庁のデータを収集し、整える

 予測するためにデータの収集から始めましょう。中々扱いやすいデータが無いので困りますが、気象庁が提供している気象データがかなり綺麗なのでこのデータを採用します。このデータは過去の気象データで地点、期間、項目(雨量、風速など)を選択しエクセルのCSVファイルでダウンロードしてくれるのでとっても便利でした。そこで平均気温(℃)、天気概況(昼:06時~18時)、降水量の合計(mm)、日照時間(時間)、平均風速(m/s)、平均蒸気圧(hPa)、平均湿度(%)、平均雲量(10分比)がかなりを取り、東京の1日毎の1年分のデータを使うことにしました。
 データを読み込んだときにいらない情報もあったのでこんな感じでデータを整えました。

Alt text
 今回は、ある日に雨が降っているかどうかを予測するので降水量の合計(mm)を正解データ(目的変数)としましょう。雨の定義としては、その日の降水量の合計が0mmより大きい、つまり少しでも降ったら雨、降水量の合計が0mmならば雨でないとします。また特徴量(線形回帰では説明変数などと呼ばれています。機械が学習するために使う要素です。)としては先程あげたデータの降水量の合計以外を使ってみます。天気概況に雨があるので人が雨と判断することは簡単です。しかし、機械は「天気が雨だと降水量が0でない」ということを判断できません。人間にとって簡単なことほど機械が行うのは難しく、天気概況を特徴量に含めた時、どのように学習してくれるか検証することも面白いですね。
 予測するためによく使われているAnacondaのjupyterNotebook、Python3.7を使います。手始めにデータを読み込んで表示させましょう。

Alt text
 もう少しデータを扱いやすく直します。天気概況ですが、文字情報のため扱いにくいです。そこで「晴」→4、「曇」→3、「雨」→2、「それ以外」(雪など)→1と数値化してしまいましょう。また、正解データになる降水量の合計に関しても降水量があれば1、なければそのまま0にしましょう。

Alt text
 上のようにデータを整えればOKです。データを整える際はプログラミングがあまり得意でなければそのままエクセルのファイルの数値を1つずつ変えれば問題ないと思います。(データ量が膨大だったらプログラミングを書きましょう。)プログラミングを書いてデータを整えるのもプログラミングの良い練習になると思います。


線形回帰で実装する

 ここまで来たらついに予測ができます。
 まずは、年月日以外のすべてのデータを説明変数、降水量の合計を目的変数として線形回帰してみましょう。今回はライブラリを使って実装します。

Alt text
 少し教師あり学習(線形回帰のように正解データがある学習)について説明します。教師あり学習ではデータを訓練用のデータと評価用のデータに分ける必要があります。
 訓練用のデータというのは機械に学習させるための材料になるものです。このデータをもとに機械は学習をします。そしてその学習をしたモデルを用いて評価用のデータを評価します。最後にその評価結果ともともとの正解データを見比べてどれだけあっているかが精度として出るわけです。

 つまり、pythonで実装する際もデータをどれくらいの割合で分けるか機械に指示する必要があります。そのライブラリがIn [9]の2行目「from sklearn.modelselection import traintest_split」です。1行目のライブラリは線形回帰のライブラリになります。
 実装した結果が次のようになりました。

Alt text
精度は、0.4781...となっているので約48%。ということはほぼ半分なので全然予測ができていません...。
 せっかく頑張っても最初はこんな感じです。ここで少しだけ上のプログラミングに関して補足します。In [11]の1行目の部分で訓練データと評価用データに分けています。今回は、「 test_size = 0.2」としているので全データに対して訓練データが80%、評価用データが20%としています。このデータは訓練データ、評価用データのどちらに割り振られるかランダムなので評価するごとに同じ結果は出ません。振り分けられ方によっては精度が上下することもあるので、正確な精度を出したいときは何回か実装してその平均を取ります。

実装結果から精度UPするために

 線形回帰をした時、精度が約52%でした。この精度になった原因としては以下が考えられます。

①過学習してしまった。
②データが少ない。
③2値に分類するのにそもそも線形回帰の相性が悪い。

以上3点が思いつきました。順番に見ていきましょう。
 まず①について、そもそも過学習というのは説明変数の相性が悪かったり説明変数が多すぎて機械が誤って学習してしまうことです。誤って学習してしまうことで、精度が悪くなってしまいます。そこで、説明変数を見直しましょう。例えば、日照時間は長ければ、気温が高くなりそうのなので気温のデータだけあれば大丈夫そうです。また、平均蒸気圧は気温に依存して決まるそうなのでこれもいらなそうです。そこで日照時間と平均蒸気圧をなくして予測してみましょう。その結果は、

Alt text

精度は、0.5585...なので多少上がりました。


 次に②については、データが少ないと機械もうまく学習することができません。1年分のデータから3年分のデータに増やして実装してみました。

Alt text

①の説明変数も減らして実装したのですが精度はほぼ変わらなかったですね。
 最後に③についてです。線形回帰は最小2乗法を用いた最もシンプルな回帰モデルですが、今回の精度の結果からあまり2値に分類するのには適したモデルではないのかもしません。そこで、2値分類する際に有名なモデルとして、ナイーブベイズやロジステック回帰です。ロジステック回帰にして実装してみましょう。

Alt text
これも①の説明変数も減らして実装すると、精度はすごく上がりました。
皆さんもいろいろ実験してみてください。

まとめ

 今回は、線形回帰を用いて雨が降るかどうかを予測しました。先程の精度UPについて考察する所では、以下のようにして精度を上げることができました。

  • 特徴量(説明変数)をうまく選ぶ

  • モデルを変えてみる

 他にもデータを見直し、もっと学習に合うようにデータを直して精度を上げます。このような作業をデータの 前処理(特徴量を選択するのも前処理です)と言います。前処理は知識やセンスが問われ、うまくできると精度がとても上がります。そして、人によってやり方も様々なので正解もありません。この精度を競うコンペも行われていて、有名所はkaggleSIGNATEがあります。コンペで優秀な成績を納めれば懸賞金も貰えるのでチャレンジしてみて下さい!

参考文献

気象庁

Anaconda

kaggle

SIGNATE


定期的にメルマガでも情報配信をしています。
ご希望の方は問い合わせページに「メルマガ登録希望」とご連絡ください。

はじめに

ニューラルネットワークの学習の目的は、損失関数( loss function )の値をできるだけ小さくするようなパラメータを見つけることに他ならない。これは言い換えれば最適なパラメータを決定するという点で最適化問題に帰着されるが、ニューラルネットワークの最適化はそのパラメータの数の多さから大変複雑な問題として扱われる。

今回はSGD, Momentum, AdaGrad, Adamと呼ばれる4つのパラメータ更新手法を紹介し、最後にMNISTデータセットを用いてこれらの更新手法を比較していく。

※Pythonで用いているコードは『ゼロから作るディープラーニング(オーム社)』を参考にしている。

SGD(Stochastic Gradient Descent)

SGD(確率的勾配降下法)は、勾配方向へある一定距離だけ進むことを繰り返すという単純な方法である。以下はSGDのparameter更新式である。(以下、Lは損失関数)

Deep Learning

但し偏微分は損失関数の勾配を表している。これをPythonのクラスとして実装すると以下のようになる。

Alt text

確かに実装は簡単であるが、関数の形状が等方向でないと非効率な経路で探索してしまうという欠点がある。例えば、

Deep Learning

という関数についてこのSGDを適用してみよう(以下この関数を用いて各手法における最適化問題を解く)。その前にこの関数の形状及び勾配を下記に示す。

SGD SGD


この勾配はy軸方向に大きく、x軸方向には小さいことが分かるだろう。しかし、今この関数の最小値は明らかに(x, y)=(0, 0)であるのに、上で示した勾配は多くの場所で(0, 0)方向を指していない。

実際、この関数にSGDを適用した結果(初期値(x, y)=(-7.0, 2.0))は以下のようなジグザグした動きをし、これはかなり非効率な経路である。

SGD

このSGDの非効率な探索経路の根本的な原因は、勾配の方向が本来の最小値ではない方向を指していることに起因している。 この欠点を改善するために、続いてMomentum, AdaGrad, Adamの3つの手法を紹介する。

Momentum

Momentumとは「運動量」という意味で物理を知っている人なら馴染みの深い言葉だろう。Momentumのparameter更新式は次のよう。

Deep Learning

Momentumは簡単に言う\とSGDに慣性項αvを付与したものである。αはハイパーパラメータであり、前回の更新量にα倍して加算することでparameterの更新をより慣性的なものにする狙いがある。

今回もPythonのクラスとしてMomentumを実装してみる。

Alt text

vは初期化時には何も保持しないが、update()が初めに呼ばれるときに、parameterと同じ構造のデータをディクショナリ変数として保持する。

ではこのMomentumを用いてさっきの関数の最適化問題を解いてみよう。結果は下図。

Momentum

SGDよりもジグザグ度合いが軽減されているのは一目瞭然であるが、一体何故だろうか。x軸、y軸についてそれぞれ考えてみよう。

x軸方向: 受ける力は小さいが、常に同じ方向に力を受けるため、同じ方向へ一定して加速する

y軸方向: 受ける力は大きいが、正と負の方向の力を交互に受け止めるため、それらが互いに打ち消し合い、y軸方向の速度は安定しない。

これよりSGDのときと比べてx軸方向により早く近づくことができジグザグの動きを軽減できる。

AdaGrad

ニューラルネットの学習においてはラーニングレートηの値が重要になってくる。この値が小さすぎると学習に時間がかかりすぎてしまうし、逆に大きすぎても最適解にたどり着かずに発散してしまう。

このラーニングレートに関する有効なテクニックとして、学習係数の減衰( learning rate decay )という手法がある。これは簡潔に言うと、学習が進むにつれてラーニングレートを小さくするというものである。

最初は"大きく"学習し、次第に"小さく"学習する手法で、ニューラルネットの学習ではよく使われる。parameter更新式は以下のよう。

Deep Learning

ここで、変数hにおいて⦿は行列の要素ごとの積を意味し、上式のように経験した勾配の値を2乗和として保持する。そして、更新の際に、hの二乗根で除算することにより学習のスケールを調整する。

これは、パラメータの要素の中でよく動いた(大きく更新された)要素は、ラーニングレートが小さくなることを表している。 実際にpythonのクラスとしてAdaGradを実装してみる。

Alt text

最後の行で1e-7を加算しているのは、self.h[key]の中に0があったときに0で除算してしまうことを防ぐためのものである。 では実際にさっきの関数にAdaGradを適用してみよう。

AdaGrad

これより、さっきのSGD, Momentumよりも効率的に最小値に向かっているのが分かる。y軸方向には勾配が大きいため最初は大きな更新ステップとなっているが、その大きな動きに比例して更新のステップが次第に小さくなるように調整される。

Adam

Adamは2015年に提案された新しい手法であり、直感的にはMomentumとAdaGradを融合したような手法である。この2つの手法のメリットを組み合わせることで、効率的にparameter空間を探索することが期待できる。

また、ハイパーパラメータの「バイアス補正」が行われているのもAdamの特徴である。(詳細はこちらADAM: A METHOD FOR STOCHASTIC OPTIMIZATION)

更新式はやや複雑であるので、今回は割愛するが勾配降下法の最適化アルゴリズムを概観するに掲載してあるので、興味のある方はぜひ見てほしい。

実際にpythonのクラスとしてAdamを実装してみる。

Alt text

Adamを用いて先の関数の最適化問題を解いてみよう。結果は以下のよう。

Adam

ソースコードは以下。

Alt text

この比較実験では、5層のニューラルネット、各隠れ層100個のノードを持つネットワークを対象にした。また活性化関数としてReLUを使用した。

上のグラフより、SGDよりも他の手法のほうが速く学習できていることは見て容易だろう。さらにAdaGradのほうが少しだけ速く学習が行われているようである。

しかし、この実験はハイパーパラメータ(ラーニングレートなど)の設定や、ニューラルネット自体の構造(何層にするか)によって結果は変わってくることに留意せよ。

しかし、一般的にはSGDよりも他の3つの手法の方が速い学習を行い、より高い認識性能を誇ることもある。

以上4つの手法を紹介したが結局どれを用いるのがベストなのか?残念ながら全ての問題で優れた手法は今のところなく、それぞれに特徴があり、得意不得意な問題があるのが現状である。

実際、多くの研究において今でもSGDは用いられているし、他3つの手法も十分に試す価値がある。結局、自分の好きなように色々試して、最良の手法を見つけていく他ないであろう。


定期的にメルマガでも情報配信をしています。
ご希望の方は問い合わせページに「メルマガ登録希望」とご連絡ください。

参考文献

『ゼロから作るディープラーニング(オーム社)』

Optimizer:深層学習における勾配法について

各手法におけるパラメータ更新経路

はじめに

クラスタリングはマーケティング手法としても使われている。
見込み顧客へ適切な施策を行うために、似た顧客同士をカテゴリ分けする必要があり、それをセグメンテーションという。
セグメンテーションのために機械学習の手法としてクラスタリングが使用されている。

k-meansクラスタリング(以下、k-means法)は複数個のデータをcentroids(重点)からの距離に応じて、あらかじめ決めたk個のクラスタに分ける非階層クラスタリング、及び、ハードクラスタリングの手法の1つである。

階層・非階層クラスタリングの詳細については以下のブログを参考にしてほしい。 (アクセルユニバース株式会社技術ブログ、富田、2019

クラスタリングのため目的変数無し(ラベル無し)の場合のみこの手法は利用可能である。本記事は「k-means法とはどういったものか」に焦点を当て、方法やその過程について書いている。そのため数学的な要素は最小限に留めている(k-means++法やシルエット関数の計算方法等)。これらの数学的アプローチは今後の記事、もしくは、後述の参考文献をご覧いただきたい。

k-means法の実行方法

まず最初に各データからそれらが属するクラスタの重点までの距離を定義しなければならない。クラスタは全データの中で似たもの同士の集合体であり、その判断は各データから重点の距離を基に行われる。これにはsquared Euclidean distance (ユークリッド距離の2乗)がよく使われる。他にもユークリッド距離を用いたクラスタ内誤差平方和(SSE)を最小化する方法や、マンハッタン距離、マハラノビス距離等も使われており、データ分析の当事者が最適だと考えるものを選ぶ。

Alt text 各データから重点のユークリッド距離の誤差を最小限にする方法の一例。引用元: (https://data-flair.training/blogs/k-means-clustering-tutorial/)

次にクラスタの数を決める。K-means法を行う際にはあらかじめ全データを何個のクラスタに分けるか定める必要がある。この個数に関して最適解は無く、データ分析者自身が判断しなければならない。極端に言えば、データ分布を観た後に直感的に個数を決めてもそれが間違っているのかどうか分からない。しかしながら、もちろんそれでは説得力に著しくかける。求められているのはクラスタ内誤差平方和(SSE)を最小化するなどして、できるだけ類似度の高いデータを集めたクラスタ数を決める。以下のような方法がより良いクラスタ数を導き出すために使われている。

・エルボー法(elbow method) SSEから判明する歪みに基づき、最も適したkの個数を導き出す方法である。歪みの最も大きくなった地点のkを最適解と見なす。下記の場合はk=4。

Alt text

・シルエット法 (silhouette method) シルエット分析(silhouette analysis)とも呼ばれ、クラスタ内のデータの密度をプロット化し、最適なkの数を判断する。シルエット係数(silhouette coefficient)は−1から1の範囲で表され、1に近いほどクラスター間の距離が遠く、しっかりとクラスターが分けられていることを表す。各バーがおよそ均等な幅になるようにクラスタ数を設定する。

Alt text

以上のような方法でクラスタ数を決めたら、各クラスタの重点が動かなくなるまでになればk-means法は終了である。

k-means法のためのPythonコード(Hastie *et al*., 2017 参照)

#ランダムにデータセットを作成。
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt
X,y = make_blobs(n_samples=300,
                 centers=4,
                 cluster_std=0.6,
                 random_state=0)
plt.scatter (X[:,0], X[:,1], c='white', marker='o', edgecolor='black', s=50)
plt.grid()
plt.tight_layout()
plt.show()

Alt text

#エルボー法を使い適したクラスタ数を求める。
from sklearn.cluster import KMeans
distortions = []
for i in range(1, 10):
    km = KMeans(n_clusters=i, 
                init='k-means++', 
                n_init=10, 
                max_iter=350, 
                random_state=0)
    y_km = km.fit_predict(X)
    km.fit(X)
    distortions.append(km.inertia_)
plt.plot(range(1, 10), distortions, marker='o')
plt.xlabel('Number of clusters')
plt.ylabel('Distortion')
plt.tight_layout()
plt.show()
#シルエット法を使い適したクラスタ数を求める。
import numpy as np
from matplotlib import cm
from sklearn.metrics import silhouette_samples

km = KMeans(n_clusters=4, 
            init='k-means++', 
            n_init=10, 
            max_iter=350,
            tol=1e-04,
            random_state=0)
y_km = km.fit_predict(X)

cluster_labels = np.unique(y_km)
n_clusters = cluster_labels.shape[0]
silhouette_vals = silhouette_samples(X, y_km, metric='euclidean')
y_ax_lower, y_ax_upper = 0, 0
yticks = []
for i, c in enumerate(cluster_labels):
    c_silhouette_vals = silhouette_vals[y_km == c]
    c_silhouette_vals.sort()
    y_ax_upper += len(c_silhouette_vals)
    color = cm.jet(float(i) / n_clusters)
    plt.barh(range(y_ax_lower, y_ax_upper), c_silhouette_vals, height=1.0, 
             edgecolor='none', color=color)

    yticks.append((y_ax_lower + y_ax_upper) / 2.)
    y_ax_lower += len(c_silhouette_vals)

silhouette_avg = np.mean(silhouette_vals)
plt.axvline(silhouette_avg, color="red", linestyle="--")

plt.yticks(yticks, cluster_labels + 1)
plt.ylabel('Cluster')
plt.xlabel('Silhouette coefficient')
plt.tight_layout()
plt.show()

シルエット係数の計算

silhouette_vals = silhouette_samples(X, y_km, metric='euclidean') y_ax_lower, y_ax_upper = 0, 0 yticks = [] for i, c in enumerate(cluster_labels): c_silhouette_vals = silhouette_vals[y_km == c] c_silhouette_vals.sort() y_ax_upper += len(c_silhouette_vals) color = cm.jet(float(i) / n_clusters) plt.barh(range(y_ax_lower, y_ax_upper), c_silhouette_vals, height=1.0, edgecolor='none', color=color) yticks.append((y_ax_lower + y_ax_upper) / 2.) y_ax_lower += len(c_silhouette_vals) silhouette_avg = np.mean(silhouette_vals) plt.axvline(silhouette_avg, color="red", linestyle="--") plt.yticks(yticks, cluster_labels + 1) plt.ylabel('Cluster') plt.xlabel('Silhouette coefficient') plt.tight_layout() plt.show()
#k-means法によるクラスタリングのプロット
from sklearn.cluster import KMeans
km=KMeans(n_clusters=4,
          init='k-means++',      
          n_init=15,
          max_iter=350,
          tol=1e-04,
          random_state=0)
y_km=km.fit_predict(X)

plt.scatter(X[y_km==0,0],
            X[y_km==0,1],
            s=50,
            c='lightgreen',
            edgecolor='black',
            marker='s',
            label='cluster 1')
plt.scatter(X[y_km==1,0],
            X[y_km==1,1],
            s=50,
            c='orange',
            edgecolor='black',
            marker='o',
            label='cluster 2')
plt.scatter(X[y_km==2,0],
            X[y_km==2,1],
            s=50,
            c='lightblue',
            edgecolor='black',
            marker='v',
            label='cluster 3')
plt.scatter(X[y_km==3,0],
            X[y_km==3,1],
            s=50,
            c='coral',
            edgecolor='black',
            marker='x',
            label='cluster 4')
plt.scatter(km.cluster_centers_[:,0],
            km.cluster_centers_[:,1],
            s=200,
            marker='*',
            c='red',
            edgecolor='black',
            label='centroids')
plt.legend(scatterpoints=1)
plt.grid()
plt.tight_layout()
plt.show()

Alt text

k-means法の欠点

  1. 初期値に依存する。
  2. 外れ値の影響を受けやすい。
  3. 各データが1つのクラスタにしか所属できない。
  4. クラスタの個数をあらかじめ決定しなければならない。(クラスタの個数に正解はない。)

さいごに

以上がk-means法についてである。今回は数学的要素を最小限に抑え、同手法がどのように成り立っているかに重きを置き紹介した。クラスタリングの中で最も有名と言っても過言ではないk-means法は、データサイエンスや機械学習をやる上で必ず目にするだろう。加えて、クラスタリングはWebマーケティングや画像処理等にも活用できるなど利用用途は幅広く知っておいて損はない手法だろう。


定期的にメルマガでも情報配信をしています。
ご希望の方は問い合わせページに「メルマガ登録希望」とご連絡ください。

参考文献

DATAFLAIR TEAM (2019) 『Data Science K-means Clustering: In-depth Tutorial with Example』Retrieved from LINK .

Hastie, Trevor et al. (2009)『The Elements of Statistical Learning: Data Mining, Inference, and Prediction Second Edition』. (杉山将 他 訳)

Pelleg, Dan and Moore, Andrew (2000) 『X-means: Extending k-means with efficient estimation of the number of clusters』. Morgan Kaufmann.

Raschka, Sebastian and Mirjalili, Vahid (2017)『Python 機械学習プログラミング』. (株式会社クイーブ 訳). インプレス.

VanderPlas, Jake (2016) 『Python Data Science Handbook: Essential Tools for Working with Data』.

平井有三(2012)『はじめてのパターン認識』. 森北出版株式会社.

このアーカイブについて

このページには、2019年10月に書かれた記事が新しい順に公開されています。

前のアーカイブは2019年8月です。

次のアーカイブは2019年11月です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。