研究室でMacBook Proが貸与されたからanyenvを入れてみる
はじめに
タイトル通り研究室でMacBook Proが貸与されてまっさらな状態だから環境構築中。pyenv入れようとしてたけどanyenvが良さげという話を聞いたので入れてみる。
anyenv
rbenv、pyenvなどのenv系ツールをまとめて管理できるツール。現状pyenvしか使ってないけどこの先他の言語使ったりするかも?なんて考えるとanyenv入れとくのはあり。ってことで入れてみる。
Githubのページはここ。
インストール
$ git clone https://github.com/riywo/anyenv.git ~/.anyenv # bash以外を利用する場合は以下適宜変更 $ echo 'export PATH="$HOME/.anyenv/bin:$PATH"' >> ~/.bash_profile $ echo 'eval "$(anyenv init -)"' >> ~/.bash_profile $ source ~/.bash_profile
使い方
$ anyenv install pyenv
$ anyenv install rbenv
$ anyenv install ndenv
...
$ source ~/.bash_profile
- その後は各envを自由に使えるぽい
ついでにpyenv
# インストールできるpythonのバージョンを確認 $ pyenv install --list # python3.6.5をインストール $ pyenv install 3.6.5 # python3系をデフォルトに $ pyenv global 3.6.5
これでPythonのデフォルトが3系になった〜〜
【Aidemy】 ディープラーニングで手書き数字を識別してみよう⑥
前回の続き。
Aidemyの画像認識コース。ちょっと進みが遅いから書くのは少なめで行く…。
ディープラーニング基礎。
深層学習の実践(手書き文字認識を例に)
深層学習とは
- ディープニューラルネットワークというモデルを使い、データの分類や回帰を行う手法
- 機械学習の1手法
分類までの流れ
- データを用意
- ニューラルネットワークモデルの構築
- モデルにデータを与え学習させる
- モデルの分類精度を評価
ディープニューラルネットワーク
- 入力層、出力層、隠れ層からなる
- 各層の縦に並んだベクトルの一つ一つの要素をノード、次元数をノード数と呼ぶ
- 出力はクラスラベルに対応する要素が1に、それ以外の値が0になる、one-hotベクトル
データの用意
- 手書き数字のデータセットであるMNIST
- 以下のようにダウンロードすることができる
from keras.datasets import mnist (X_train, y_train), (X_test, y_test) = mnist.load_data()
モデルの生成
- Kerasの
Sequential
を用いる
from keras import Sequential from keras.layers import Activation from keras.layers import Dense from keras.optimizers import sgd # インスタンス作成 model = Sequential() # ユニット数128の全結合層を追加 model.add(Dense(128)) # 活性化関数を追加 model.add(Activation('sigmoid')) # コンパイル model.compile(optimizer=sgd, loss='categorical_crossentropy', metrics=['accuracy'])
モデルの学習
verbose=1
で学習の過程を出力できる
history = model.fit(X_train, y_train, verbose=1, epochs=10)
モデルの評価
score = model.evaluate(X_test, y_test, verbose=1)
モデルによる分類
predict
メソッドを用いて予測値を取得できる- 最後の活性化関数が
softmax
などであれば各クラスに属する確率のリストが出力される - 以下のようにすることで予測したクラスラベルがわかる
pred = np.argmax(model.predict(X_test[0]))
深層学習のチューニング
ハイパーパラメータ
- 活性化関数、隠れ層の数・チャンネル数、ドロップアウト率、学習率、最適化関数、誤差関数、バッチサイズ、エポック数
ネットワーク構造
- 隠れ層の数、隠れ層のユニット数は自由に決められる
- 隠れ層やユニット数を多くすると多彩な関数が表現できるが、入力層に近い重みを適切に更新するのが難しく学習が進みにくい
- 理論で裏付けて決めることが難しく、似たような実装例を参考にするなど経験に基づいて決定される傾向がある
ドロップアウト
- ユニットの一部が学習の0で上書きされる
- 特定のニューロンの存在に依存できなくなり、より汎用的な特徴を学習するようになる
活性化関数
- ReLU(ランプ関数)
損失関数
- 学習時にモデルの出力と教師データとの差を評価する関数
- 誤差逆伝搬法で損失関数を最小化するように重みを更新
- 二乗誤差:連続値の評価に優れ、主に回帰モデルの誤差関数として使われる
- クロスエントロピー誤差:二値分類の評価に特化しているため、主に分類モデルの誤差関数として使われる
最適化関数
- 重みの更新は誤差関数を書く重みで微分した値を元に更新すべき方向とどの程度更新するかを決める
- 微分によって求めた値を学習率、エポック数、過去の重みの更新料などを踏まえてどのように重みの更新に反映するかを定めるのが最適化関数
学習率
- 各層の重みを一度にどの程度変更するかを決めるハイパーパラメータ
バッチサイズ
反復学習
- モデルの精度を上げるために同じ訓練データを使って何度か学習させること
- 学習を行う回数をエポック数という
【Aidemy】 ディープラーニングで手書き数字を識別してみよう⑤
前回の続きから。
Aidemyの画像認識コース。
今回は教師あり学習(分類)。
教師あり学習(分類)の基礎
分類とは
- 蓄積されたデータを元に機会が新しいデータの分類を行うこと
- 予測される値はデータのカテゴリであり、離散値
分類の種類
- 二項分類(二値分類、2クラス分類)
分類するカテゴリが2つの分類問題のことで、直線でクラス間を識別できる場合は線形分類、そうでない場合は非線形分類という - 多項分類(他クラス分類)
クラスが3つ以上の分類問題のこと。単に直線では識別できない場合が多い。
分類のながれ
- データの前処理 データの整形、操作
- モデルの選択 分類器の選択
- モデルの学習 チューニングをするハイパーパラメータの選択 / パラメータのチューニング
- モデルによる予測 未知のデータを使ってモデルの精度検証 / WEBサービスなどに組み込み、AIモデルを実運用
データの用意
- skelearn.datasetsモジュールの
make_classification()
関数を利用 - 引数は以下の通り
- n_sampels:用意するデータの個数
- n_classes:クラス数。デフォルトは2。
- n_features:データの特徴量の個数
- n_redundant:分類に不要な特徴量(余分な特徴量)の個数
- random_state:乱数のシード
from sklearn.datasets import make_classification x, y = make_classification(n_samples=XX, n_classes=XX, n_features=XX, n_reducdant=XX, random_state=XX)
学習と予測
from sklearn.linear_model import LogisticRegression from sklearn.svm import LinearSVC, SVC from skelarn.tree import DecisionTreeClassifier from sklearn.ensemble import RandomForestClassifier from sklearn.neighbors import KNeighborsClassifier model = Classifier() # 上でインポートした分類器から選択 model.fit(train_X, train_y) # モデルの学習 model.predict(test_X) # モデルによるデータの予測 model.score(test_X, test_y) # モデルの正解率
分類手法
ロジスティック回帰
- 線形分離可能なデータの境界線を学習によって見つけてデータの分類を行う
- データがクラスに分類される確率も計算することが可能
- 今日データから学習した境界線はクラスの端にあるデータのすぐそばを通るようになるため、汎化能力が低いという欠点がある。
- 実装
from sklearn.linear_model import LogisticRegression import matplotlib.pyplot as plt import numpy model = LogisticRegression() model.fit(train_X, train_y) model.predict(test_X) model.score(test_X, test_y) # 可視化 plt.scatter(X[:, 0], X[:, 1], c=y, marker='.', cmap=matplotlib.cm.get_cmap(name='bwr'), alpha=0.7) xb = np.linspace(-10, 10) yb = -model.coef_[0][0] / model.coef_[0][1] * \ xb - model.intercept_ / model.coef_[0][1] plt.plot(xb, yb) # 諸々グラフ設定をして plt.show()
線形SVM(サポートベクターマシン)
- ロジスティック回帰と同じくデータの境界線を見つけることでデータの分類を行う。
- クラスごとの境界線に最も近いデータと境界線の距離を表すベクトルをサポートベクターと呼び、サポートベクターの距離の総和を最大化しようとする(マージン最大化)ことによって境界線を決定する。
- 境界線が2クラス間の最も離れた場所に惹かれるため一般化されやすい。
- サポートベクターのみを考えれば良いため、筋道が立ちやすい
- データ量が増えると計算量が経て学習や予測が遅くなる傾向がある
- 実装
from sklearn.svm import LinearSVC model = LinearSVC() model.fit(train_X, train_y) model.score(test_X, test_y)
非線形SVM
- 特徴カーネル関数と呼ばれる変換式に従ってデータを操作することでデータが線形分離可能な状態になる場合があり、そのような処理を行ってくれるモデルが存在する
- カーネル関数による操作はその操作の結果を知らずに分類を行うことが可能で、カーネルトリックとも呼ばれる
- 実装
from sklearn.datasets import make_gaussian_quantiles from sklearn.model_selection import train_test_split from sklarn.svm import SVC X, y = make_gaussian_quantiles(n_samples=1000, n_classes=2, n_features=2, random_state=42) train_X, test_X, train_y, test_y = train_test_split(X, y, random_state=42) model = SVC() model.fit(train_X, train_y) model.score(test_X, test_y)
決定木
- データの要素(説明変数)の一つ一つに着目し、その要素内でのある値を境にデータを分割していくことでデータのぞkするクラスを決定しようとする手法
- 説明変数の一つ一つが目的変数にどのくらいの影響を与えているのかを見ることができる
- 分割を繰り返すことで枝分かれするが、先に分割される変数ほど影響力が大きいと捉えることができる
- 線形分離可能なデータが苦手 / 学習が教師データに寄り過ぎるという欠点あり
- 実装
from sklearn.tree import DecisionTreeClassifier model = DecisionTreeClassifier() model.fit(train_X, train_y) model.score(test_X, test_y)
ランダムフォレスト
- 決定木の簡易版を複数作成し、分類の結果を多数決で決める
- アンサンブル学習の一手法。
- 一つ一つの決定木はランダムに決められた小数の説明変数だけを用いてデータの属するクラスを決定しようとする
- 複数の分類器をとして多数決により結果を出力するため、外れ地によって予測結果が左右されにくい
- データが少ないと二分木の分割ができず、予測の精度が下がってしまう
from sklearn.ensemble import RandomForestClassifier model = RandomForestClassifier() model.fit(train_X, train_y) model.score(test_X, test_y)
k-NN(k近傍法)
- 予測するデータと類似したデータをいくつか見つけ、多数決により分類結果を求める
- 怠惰学習と呼ばれる学習の一手法であり、学習コストが0
- 教師データから学習するわけではなく、予測時に教師データを直接参照してラベルを予測する
- 予測手順
- 教師データを予測に用いるデータとの類似度で並べ直し
- 分類器に設定されたk個分のデータを類似度の高い順に参照
- 参照された教師データが属するクラスの中で最も多かったものを予測結果として出力
- アルゴリズムは単純だが高い予測精度が出やすい
- 複雑な形の境界線も表現しやすい
- kを大きくし過ぎると識別範囲の平均化が進み予測精度が下がってしまう
- 実装
from sklearn.neighbors import KNeighborsClassifier model = KNeighborsClassifier() model.fit(train_X, train_y) model.score(test_X, test_y)
ハイパーパラメーターとチューニング
ハイパーパラメーターとは
- 機械学習のモデルが持つパラメーターの中で人が調整をしないと行けないパラメーターのこと
チューニングとは
- ハイパーパラメーターを調整すること
- 直接値をモデルに入力
- 値の範囲を指定して最適な値を探してもらう
ロジスティック回帰
- C
- モデルが学習する識別境界線が教師データの分類間違いに対してどれくらい厳しくするかの指標
- Cの値が大きいと教師データをより精度よく分類できるが、過学習もしやすくなる
- Cの値が小さいと一般化された境界線を得やすくなるが、外れ値の少ないデータでは境界線がうまく識別できない
- sklearnにおける初期値は1.0
- penalty
- モデルの複雑さに対するペナルティ
- L1/L2が入る
- L1:データの特徴量を削減することで識別境界線の一般化を測るペナルティ
- L2:データ全体の重みを減少させることで識別境界線の一般化を測るペナルティ
- 基本的にはL2を選べばOK
- multi_class
- 多クラス分類を行う際にモデルがどういった動作を行うか
- ovr/multinomial
- ovr:クラスに対して「属する/属さない」の二値で答えるような問題に適する
- multinomial:各クラスに分類される確率も考慮
- random_state
- 学習の際のデータの処理順を制御する
- 再現性を考えて固定するのが良い
線形SVM
- C / penalty
- ロジスティック回帰と同様
- multi_class
- 多項分類を行う際にモデルがどういった動作を行うか決める
- ovr / crammer_singer
- 基本的にはovrの方が動作が軽く結果が良い
- random_state
非線形SVM
- C
- ソフトマージンのペナルティ
- 学習時に分類の誤りをどの程度許容するかを指定するパラメーター
- kernel
- 受け取ったデータを操作して分類しやすい形にするための関数を定義
- linear / rbf / poly / sigmoid / precomputed
- linear:線形SVM。特殊な理由がない限りLinearSVCを使うべき
- rbf / poly:立体投影の・ようなもの。rbfは比較的高い正解率が出ることが多いので通常はデフォルトであるrbfを使用
- precomputed:データが前処理によってすでに整形済みの場合に使用
- sigmoid:ロジスティック回帰モデルと同じ処理
- decision_function_shape
- SVCにおけるmulti_classパラメータ
- ovo / ovr
- ovo:クラス同士のペアを作り、そのペアで2項分類を行い多数決で属するクラスを決定。計算量が多くデータ量の増大によっては動作が重くなることも
- ovr:一つのクラスとそれ以外という分類を行い多数決で属するクラスを決定
- random_state
- データの前処理に関係するパラメータ
- 以下のように指定する
import numpy as np from sklearn.svm import SVC # 乱数生成器を構築 random_state = np.random.RandomState() model = SVC(random_state=radom_state)
決定木
- max_depth
- 学習時にモデルが学習する気の深さの最大値
- 設定されたいない場合、木は教師データの分類がほぼ終了するまでデータを分割 -> 過学習
- 木の高さを制限すること = 決定木の枝刈り
- random_state
- 決定木の分割の際、データの分類を説明できる要素の値を見つけてデータの分割を行う
- そのような値の候補を乱数の生成により決定
ランダムフォレスト
- n_estimators
- 簡易決定木の個数
- max_depth
- 簡易決定木に関するパラメーター
- 通常の決定木より小さな値を入力する
- random_state
- 結果の固定のみならず、決定木のデータの分割や用いる要素の決定など多くの場面で乱数が寄与するため、重要なパラメーター
k-NN
- n_neighbors
- k-NNのkの値のこと
- 結果予測の際に使う類似データの個数を決める
チューニングの自動化
- グリッドサーチ
- 調整したいハイパーパラメーターの値の候補を明示的に複数指定、パラメーターセットを作成してその時のモデルの評価を繰り返すことでモデルとして最適なパラメーターセットを作成す
- 数学的に連続ではない値を取るパラメーターの探索に向いている
- 多数のパラメーターを同時にチューニングするには不向き
- 実装
from sklearn.model_selection import GridSearchCV from sklearn.svm import SVC param = { 'C': [10 ** -i for i in range(5)], 'kernel': ['linear', 'poly', 'rbf', 'sigmoid'], 'random_state': [42], } svm = SVC() # グリッドサーチ clf = GridSearchCV(svm, param) clf.fit(train_X, train_y) # パラメーターサーチ結果の取得 best_param = clf.best_params_ print(clf.socre(test_X, test_y), best_param)
- ランダムサーチ
- パラメーターが取りうる値の範囲を指定し、確率で決定されたパラメータセットを用いてモデルの評価を行うことを繰り返すことによって最適なパラメーターセットを探す
- 値の範囲の指定はパラメーターの確率関数を指定
- 実装
from scipy import stats from sklearn.model_selection import RandomizedSearchCV from skelarn.svm import SVC param = { # 0から100までの一様確率変数 'C': stats.uniform(loc=0.0, scale=100.0), # 乱数で選ぶ必要がないものはリストで指定 'kernel':['linear', 'poly', 'rbf', 'sigmoid'], 'random_state': [42], } svm = SVC() # ランダムサーチ clf = RandomizedSearchCV(svm, param) clf.fit(train_X, train_y) best_param = clf.best_params_ # 以下略
【Aidamy】 ディープラーニングで手書き文字を識別してみよう④
前回に引き続き。
Aidemyの手書き文字認識コースをやっていく。
今回は機械学習概論。
機械学習概論
機械学習が今注目される理由
- 人間では到底実現不可能な時間で、大量のデータから自動的に短時間で正確な結果を得ることができる
- 画像、音声、マーケティング、自然言語、医療など様々な分野で進化を発揮
- コンピュータの処理速度が向上し、豊富なデータの解析に耐えうるデバイスが登場
機械学習とは
- データから反復的に学習し、そこに潜むパターンを探しだすこと
- 機械学習は大きく3つに分けられる
- 教師あり学習(Supervised Learnings)
- 教師なし学習(Unspervised Learnings)
- 強化学習(Reinforcement Learnings)
教師あり学習
- 「教師」とは「データに付随する正解ラベル」のこと
- データと正解ラベルの例
- 手書き数字画像 -> その画像が表す数字
- 動物の写真 -> 写っている動物
- 文章 -> 作者
- ある部屋に関する定量的な条件 -> 家賃
- 上3つの様にカテゴリを予測するものは「分類問題」
- 一番下の様に数値を予測するものは「回帰問題」
- 教師あり学習の流れ
- 様々なデータをコンピュータに与え、「正解ラベル」を学習し、「正解ラベル」を出力するようにモデルを学習
- 学習したモデルに未知のデータを適用した時に【正解ラベル」に近い値が出るかどうか検証
教師なし学習
- 与えられたデータから規則性を発見し、学習する手法
- 予め答えが与えられないため、正解や不正解がない
- 以下の様な場面で用いられる
- おすすめの商品やメニューを推薦するレコメンデーション
- 多次元データを人間が可視化しやすいように圧縮
- 自然言語処理などの分野で情報を圧縮
強化学習
- 「エージェント」と「環境」が存在
- エージェントは環境に対してある行動をする
- その結果として環境がエージェントに報酬を与える
- エージェントはその報酬に基づいて次の行動を決定
- 深層学習と組み合わせて用いられ、囲碁や将棋、ロボットの操作制御などで用いられている
- 正解ラベルがなくてもコンピュータが自動的に「良い」評価になるような動き方を学習する
機械学習の流れ
全体の流れ
データの学習
- 例えばデータを直線で2つに分けるタスクであれば、ある線を引き、妥当かどうかを確認し、修正、また線を引く、ということを反復し、その結果として正しい線が引けるように鳴る
- コンピュータ自身が、自分で答えを見つけ、データのパターンから作られた基準をモデルという
学習データとテストデータ
- 学習に用いられるデータを学習データ(トレーニングデータ)、精度評価に用いられるデータをテストデータと呼ぶ
- テストデータは未知のデータである必要がある
ホールドアウト法
- 与えられたデータセットをトレーニングデータとテストデータの2つに分割するシンプルな手法
scikit-learn
のtrain_test_split
を用いれば以下の様に分割できるtest_size
でテストデータの割合を指定random_state
を指定すると毎回同じ様にテストデータが分割される
from sklearn import datasets from sklearn.model_selection import train_test_split iris = datasets.load_iris() x = iris.data y = iris.target x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=0)
k-分割交差検証(クロスバリデーション)
- トレーニングデータセットをk分割し、そのうちのk-1個のデータを学習用のデータセットとして用い、残りの1個をモデルのテストに用いる手法
- k回の学習と評価を繰り返し、そのk個の性能評価の平均を酉、平均性能を算出する
- データセットが小さい場合、分割の個数をデータセットと同じ数にして行う「一個抜き」(Leave-One-Out: LOO)交差検証が推奨される
sklearn
のcross_validation
を用いることで実行できる- 以下はSVMの例
import numpy as np from sklearn import svm, datasets, cross_validation iris = datasets.load_iris() x = iris.data y = iris.target svc = svm.SVC(c=1, kernel='rbf', gamma=0.001) scores = cross_validation.cross_val_score(svc, x, y, cv=5)
過学習(オーバーフィッティング)
- 与えられたデータに適用しすぎてしまい、正しい基準が構築されないこと
- 過学習の解決手段
- 逆にデータを学習できていない状態を学習不足と呼ぶ
- 過学習を起こしているモデルのことをバリアンスが高いという
- 学習不足を起こしているモデルのことをバイアスが高いという
アンサンブル学習
- 複数のモデルに学習させることによってデータの一般化を獲得しようとする試み
- 2種類の手法が存在
- バギング:複数のモデルを同時に学習させ、予測結果の平均を取ることで予測結果の汎化を試みる
- ブースティング:モデルの予測結果に対するモデルを作成し汎化性能を高める
性能評価指標
混同行列
- 学習済みモデルがどの程度良いものであるかを判断する評価指標の一つ
- 書くテストデータに対する予測結果を真陽性(True Positive: TP)、真陰性(True Negative: TN)、偽陽性(Falase Positive: FP)、偽陰性(False Negative: FN)の④つの観点で分類し、それぞれに当てはまる予測結果の個数をまとめた表
- 「真か偽」は予測が的中したかどうか/「陽性か陰性」は予測されたクラス
sklearn.metrics
モジュール内のconfusion_matrix
関数を利用して実装
from sklearn.metrics import confusion_matrix # 0: 陽性 / 1: 陰性 y_true = [0, 0, 0, 1, 1, 1] y_pred = [1, 0, 0, 1, 1, 1] confmat = confusion_matrix(y_true, y_pred) print(confmat) # Output # [[2 1] # [0 3]]
正解率
- すべての事象の中で予測結果があっていた数の割合
[tex: 正解率=\frac{TP+TN}{FP+FN+TP+TN}]
F値
- データに偏りがある状態で正解率という指標を使うのは危険
- そこで適合率/精度(precision)、再現率(recall)、F値という値が用いられる
- 適合率/精度(precision):陽性と予測されたデータのうち、実際に陽性であるものの割合
- 再現率:実際の陽性データのうち陽性と予測できたものの割合
- F値:適合率と再現率の調和平均
- どの指標も0~1の範囲で示され、1に近い方が性能が良い
[tex: 適合率=\frac{TP}{FP+TP}] [tex: 再現率=\frac{TP}{FN+TP}] [tex: F値=2\frac{適合率\times再現率}{適合率+再現率}]
性能評価指標の実装
from sklearn.metrics import precision_score, recall_score, f1_score y_true = [0, 1, 1, 0, 1] y_pred = [1, 0, 1, 1, 1] precision_score(y_true, y_pred) recall_score(y_true, y_pred) f1_score(y_true, y_pred)
再現率と適合率の関係
- 2つの性能評価指標はトレードオフの関係にある
- 具体例
PR曲線
- 横軸を再現率縦軸を適合率としてデータをプロットしたグラフを表したもの
- 陽性と識別されたデータに対して一つずつ適合率と再現率を計算
PR曲線を用いたモデルの評価
- 適合率と再現率はトレードオフだが、適合率と再現率が一致するブレークイーブンポイント(BEP)が存在する
- BEPは適合率と再現率の関係をバランスよく保ったままコストと利益を最適化できるので、ビジネス上重要な点
- PR曲線においてBEPが右上に遷移するほどよいモデル
しばらく放置しちゃったから時間かかったけどなんとか終わった、、、。
【Aidamy】 ディープラーニングで手書き文字を識別してみよう③
前回に引き続き、Aidamyの手書き文字認識コースをやっていく。
今回はコース4:データクレンジング。
データクレンジング
機械学習モデルにデータを読みこませるために、データの欠損値や画像の前処理などを行う手法のこと。
lambdaやmapなどの便利なPython記法
無名関数lambda
lambda '引数': '返り値'
で関数を作成することができる。- 引数を取らず'Hello'と出力するだけの関数などは表現することができない。
# func1とfunc2は同じ func1 = lambda x: x ** 2 + x + 3 def func2(x): return x ** 2 + x + 3 # func3とfunc4は同じ # 引数は複数取れる func3 = lambda x, y, z: x * y + z def func4(x, y, z): return x * y + z # func5とfunc6は同じ # ifによる条件分岐を一行でかける func5 = lambda x: x ** 2 + 3 if 0 <= x < 10 else x def func6(x): if 0 <= x < 10: return x ** 2 + 3 else: return x # 以下のように関数名をつけなくても実行できる (lambda x, y: x ** 2 + 3 * y)(3, 8)
listの分割
test_sentence = "this,is a.test sentence" # 通常のsplit test_sentence.split(' ') #=> ["this,is" "a.test" "sentence"] # 複数の記号で分割したい時 import re re.split('[, .]', test_sentence) #=> ["this" "is" "a" "test" "sentence"]
高階関数map
lst = [1, -2, -3, 4, -5] list(map(abs, lst)) #=> [1 2 3 4 5]
filter関数
filter(条件となる関数, 配列)
でlistの各要素から条件を満たす要素だけを取り出す- 条件となる関数は入力に対して
True/False
を返す関数
lst = [1, -2, -3, 4, -5] list(map(lambda x: x > 0, lst)) #=> [1 4]
sorted関数
- sort関数よりも自由度の高いソートができる
sorted(配列, key=キーとなる関数, reverse=False)
nest_list = [ [0, 9], [1, 8], [2, 7], [3, 6], [4, 5] ] # 第二要素をキーとしてソート sorted(nest_list, key=lambda x: x[1]) #=> [[4, 5], [3, 6], [2, 7], [1, 8], [0, 9]]
内包表記
- イテレータを作成するときは
map
やfilter
、リストを作成するときは内包表記
[abs(x) for x in lst] # list(map(abs, lst)) [x for x in lst if x > 0] # list(filter(lambda x: x > 0, lst))
- 多重ループも内包表記で表現することができる
xy_list = [[x, y] for x in x_list for y in y_list] # 以下と同じ xy_list = [] for x in x_list: for y in y_list: xy_list.append([x, y])
defaultdict
- 通常のdict型と同じ様に使える
- 辞書のデフォルト値を決めておくことができる
- デフォルトをintに
from collections import defaultdict lst = ['a', 'b', 'b', 'a', 'b', 'c', 'd'] d = defaultdict(int) for key in lst: d[key] += 1 print(d) #=> defaultdict(<class 'int'>, {'a': 2, 'b': 3, 'c': 1, 'd': 1}) # キーでソート sorted(d.items()) #=> [('a', 2), ('b', 3), ('c', 1), ('d', 1)] # 値で降順ソート sorted(d.items(), key=lambda x:x[1], reverse=True) #=> [('b', 3), ('a', 2), ('c', 1), ('d', 1)]
- デフォルトをlistに
from collections import defaultdict # まとめたいデータprice...(名前, 値段) price = [ ("apple", 50), ("banana", 120), ("grape", 500), ("apple", 70), ("lemon", 150), ("grape", 1000) ] d = defaultdict(list) for k, v in price: d[k].append(v) print(d) print([sum(v) / len(v) for v in d.values()]) # 出力 #=> defaultdict(<class 'list'>, {'apple': [50, 70], 'banana': [120], 'grape': [500, 1000], 'lemon': [150]}) #=> [60.0, 120.0, 750.0, 150.0]
Counter
- 要素の数え上げに特化したクラス
from collections import Counter lst = ['a', 'b', 'b', 'a', 'b', 'c', 'd'] d = Counter(lst) print(d) #=> Counter({'b': 3, 'a': 2, 'c': 1, 'd': 1}) # ソートして上位を出力 print(d.most_common(2) #=> [('b', 3), ('a', 2)]
DataFrameを用いたデータクレンジング
Pandas
import pandas as pd # アヤメデータを取得 iris_df = pd.read_csv('http://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data', header=None) # カラム名を指定 iris_df.columns = ["sepal length", "sepal width", "petal length", "petal width", "class"] # 2つのデータを'ID'列でソートし、行番号を振りなおして連結 df1.append(df2).sort_values(by='ID').reset_index(drop=True) # リストワイズ削除 # データ欠損のある行をまるごと消去 df.dropna() # ペアワイズ削除 # データ欠損値の少ない列をだけを残す df[[0, 2]].dropna() # データの欠損値を0埋め df.fillna(0) # データの欠損値を前の値で埋める df.fillna(method='ffill') # データの欠損値をデータの平均値で埋める(平均値代入法) df.fillna(df.mean()) # 重複のある行にTrueと表示 df.duplicated() # 重複データ削除後のデータを表示 df.drop_duplicates() # マッピング # 共通のキーとなるデータに対してテーブルからそのキーに対応するデータを持ってくる area_map ={"Tokyo":"Kanto" ,"Hokkaido":"Hokkaido" ,"Osaka":"Kansai" ,"Kyoto":"Kansai"} city_df['region'] = city_df['city'].map(area_map) #=> 新たに'region'列ができてそれぞれの地域が追加される # bin分割 df_cut = pd.cut(df, bin_num) # 各ビンの数を集計 pd.value_counts(df_cut)
CSV
import csv # csvファイルの作成 with open('students.csv', 'w') as f: writer = csv.writer(f, lineterminator='\f') writer.writerow(['name', 'age', 'school']) writer.writerow(['Taro', 18, 'Handa']) writer.writerow(['Hanako', 16, 'Taketoyo']) writer.writerow(['Toranosuke', 28, 'Azabu']) writer.writerow(['Bakabon', 102, 'Kaisei']) writer.writerow(['Korosuke', 2, 'Asahigaoka'])
OpneCVの利用と画像データの前処理
RGBデータ
- 画像はピクセルと呼ばれる小さな粒の集まりで表現される。
- それぞれのピクセルの色を変えて画像を表現。
- カラー画像はRed, Green, Blue(RGB)で表現。
- 三色の明るさは多くの場合0~255(8bit)の数値
- (255, 0, 0)は赤、(0, 0, 0)は黒、(255, 255, 255)は白
- OpenCVでは一つのピクセルを表すための要素の数をチャンネル数と呼ぶ
- RGB画像はチャンネル数3
- モノクロ画像はチャンネル数1
画像データのフォーマット
- PNG、JPEG、PDF、GIFなどがある
- 可逆圧縮、非可逆圧縮の違い、容量の違いなど一長一短
- JPEG、GIF、PNG、TIFF、BMP…いろいろな画像ファイルと特徴とか参考に
OpenCV
- 画像を扱うのに便利なライブラリ
- インポート
import numpy as np import cv2
- 画像の作成と保存
# 画像の作成、保存 # 画像サイズ img_size = (512, 512) # 緑色の画像 my_img = np.array([[[0, 255, 0] for _ in range(img_size[1])] for _ in range(img_size[0])]) # 画像の保存 cv2.imwrite('my_green_img.jpg', my_img)
my_green_img.jpg
- 画像の読み込みと表示
# 画像の読み込み img = cv2.imread('./sample.jpg') # 画像の表示 # sampleはウィンドウの名前 cv2.imshow('sample', img)
sample.jpg
- トリミングとリサイズ
size = img.shape # 行列の一部を取り出してくればトリミングできる trimed_img = img[:size[0] // 2, :size[1] // 3] # リサイズ resized_img = cv2.resize(trimed_img, (trimed_img.shpe[1] * 2, trimed_img.shape[0] * 2))
trimed_img.jpg
resized_img.jpg
- 回転・反転
img = cv2.imread("./sample.jpg") # warpAffine()を用いるのに必要な行列を作成 # 第一引数:回転の中心(今回は画像の中心) # 第二引数:回転角度(今回は180度) # 第三引数:倍率(今回は2倍に拡大) mat = cv2.getRotationMatrix2D(tuple(np.array(img.shape[:2]) / 2), 180, 2.0) # アフィン変換 # 第一引数:変換したい画像 # 第二引数:上で生成した行列(mat) # 第三引数:サイズ affine_img = cv2.warpAffine(img, mat, img.shape[:2]) # 反転 # 第二引数:反転の軸(0: x軸 / 正: y軸 / 負: xy両方) fliped_img = cv2.flip(img, 0)
affine_img.jpg
fliped_img.jpg
- 色調変換・色反転
- RGB -> Lab色空間
- Lab色空間は人間の視覚に近似するよう設計されている
# Lab色空間に変換 lab_img = cv2.cvtColor(img, cv2.COLOR_RGB2LAB) # モノクロ画像に変換 gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) # 画像の色を反転 inverted_img = cv2.bitwise_not(img)
lab_img.jpg
gray_img.jpg
inverted_img.jpg
OpenCVの利用
閾値処理(二値化)
- 画像の容量を小さくするために、一定以上明るいもの、あるいは一定以上暗いものをすべて同じ値にする処理
cv2.threshold()
で実現可能- 第一引数:処理する画像
- 第二引数:しきい値
- 第三引数:最大値
- 第四引数:二値化の仕方
threshold, threshold_img = cv2.threshold(img, 75, 255, cv2.THRESH_BINARY)
threshold_img.jpg
マスキング
- 画像の一部分のみをとりだす
- 白黒でチャンネル数1のマスク用画像を容易
- ある画像のマスク用画像の白い部分と同じ部分だけ抽出
mask = cv2.imread('mask.png', 0) # 第二引数を0にするとチャンネル数1に変換して読み込み mask = cv2.resize(mask, (img.shape[1], img.shape[0])) masked_img = cv2.bitwise_and(img, img, mask = mask)
mask.png
masked_img.jpg
ぼかし
blur_img = cv2.GaussianBlur(img, (21, 21), 0)
blur_img.jpg
ノイズの除去
cv2.fastNlMeansDenoisingColord()
を使う
膨張・収縮
- 主に2値画像で行われる
- あるピクセルを中心とし、フィルタ内の最大値をその中心の値にすることを膨張、最小値をその中心の値にすることを収縮という
- フィルタは、中心のピクセルの上下4つを用いる方法と、自信を囲む8つを用いる方法の2通りが主。
- uint8は8ビットで表された符号なしの整数
filt = np.array([[0, 1, 0], [1, 0, 1], [0, 1, 0]], np.uint8) # 膨張 dilated_img = cv2.dilate(img, filt) # 収縮 eroded_img = cv2.erode(img, filt)
dilated_img.jpg
eroded_img.jpg
長かった、、、。
【Aidamy】 ディープラーニングで手書き文字を識別してみよう②
前回記事の続き。
Aidamyの手書き文字認識コースをやっていく。
今回はコース3:Matplotlibによるデータの可視化から。
Matplotlibによるデータの可視化
データ可視化のための準備
シードの設定
シードを固定すれば常に同じ乱数列を生成することができる
np.random.seed(整数)
正規分布に従う乱数
np.random.randn()
二項分布に従う乱数
以下は確率0.5で成功する試行を100回行った時の成功数をsize=10000個分求める
np.random.binomial(100, 0.5, size=10000)
リストからランダムに選択
np.random.choice(list, int)
datetime型
datetime.datetime(年, 月, 日, 時, 分, 秒, ミリ秒)
を指定するとdatetime型を返すtimedelta型
datetime.timedelta(日, ..., ミリ秒)
で時間の長さを表すtimedelta型を返す
datetime型との演算が可能文字列からdatetime型生成
2018-02-19 16-15-30
の様に日時が表されている時、以下のようにdatetime型に変換できる
datetime.datetime.strptime(s, "%Y-%m-%d %H-%M-%S")
等間隔の数列生成
np.arange(0, 11, 2)
のようにすることで0から10までの偶数列を生成
np.linspace(0, 10, 5)
のようにすることで[0. 2.5 5 7.5 10.]
を生成
matplotlibの使い方
import maplotlib.pyplot as plt import numpy as np x = np.linspace(0, 2*np.pi) y1 = np.sin(x) y2 = np.cos(x) # 画像サイズを4インチ×4インチに設定 plt.figure(figsize=(4, 4)) # x軸、y軸の範囲を指定 plt.xlim([0, 2*np.pi]) plt.ylim([0, 1]) # タイトルを設定 plt.title('TITLE') # x軸とy軸の名前を設定 plt.xlabel('x-axis') plt.ylabel('y-axis') # グリッドを表示 plt.grid(True) # 目盛りを設定 plt.xticks(目盛りを挿入する位置, 挿入する目盛り) # x, y1を赤でプロット plt.plot(x, y1, color='r', label='y=sin(x)') # x, y2を青でプロット plt.plot(x, y2, color='b', label='y=cos(x)') # 上記で設定したラベルに基づいて凡例を表示 plt.legend() # グラフを表示 plt.show()
- subplotを用いる場合
import matplotlib.pyplot as plt import numpy as np x = np.linspace(0, 2*np.pi) y = np.sin(x) # Figureオブジェクトを作成 fig = plt.figure(figsize=(9, 6)) # 2×3のレイアウトの2行目2列目にサブプロットオブジェクトを作成 ax = fig.add_subplot(2, 3, 5) # subplot周りの余白調整 plt.subplots_adjust(wspace=横間隔を空ける割合, hspace=縦間隔を空ける割合) # subplotのy軸の表示範囲設定 plt.set_ylim([0, 1]) # subplotのタイトルやx軸、y軸の名前の設定 ax.set_title('TITLE') ax.set_xlabel('x-axis') ax.set_ylabel('y-axis') # subplotにグリッドを表示 ax.grid(True) # subplot内のグラフの軸に目盛りを設定 ax.set_xticks('挿入位置のリスト') ax.set_xticklabels('目盛りのリスト') # データx,yをグラフにプロット ax.plot(x,y) # 空白部分をサブプロットで埋める axi = [] for i in range(6): if i == 4: continue fig.add_subplot(2, 3, i+1) plt.show()
様々なグラフを作る
import numpy as np import matplotlib.pyplot as plt
以下、上記のインポートがしてあるとして進めていく。
折れ線グラフ
days = np.arange(1, 11) weight = np.array([10, 14, 18, 20, 18, 16, 17, 18, 20, 17]) # 表示設定 plt.ylim([0, weight.max()+1]) plt.xlabel("days") plt.ylabel("weight") # 赤の円マーカーの青の破線の折れ線グラフをプロット plt.plot(days, weight, linestyle='--', color='b', marker='o', markerfacecolor='r') plt.show()
棒グラフ
x = [1, 2, 3, 4, 5, 6] y1 = [12, 41, 32, 36, 21, 17] y2 = [43, 1, 6, 17, 17, 9] labels = ["Apple", "Orange", "Banana", "Pineapple", "Kiwifruit", "Strawberry"] # ラベル付きの棒グラフを作成 plt.bar(x, y1, tick_label=labels) # bottom(下の余白)にy1を設定することで積み上げ棒グラフに plt.bar(x, y2, bottom=y1) # 系統ラベルを設定 plt.legend(("y1", "y2")) plt.show()
ヒストグラム
np.random.seed(0) data = np.random.randn(10000) fig = plt.figure(figsize=(15, 5)) ax1 = fig.add_subplot(1, 2, 1) ax2 = fig.add_subplot(1, 2, 2) # 正規化されたビン数自動設定のヒストグラム ax1.hist(data, bins='auto', normed=True) # 正規化されたビン数100の累積ヒストグラム ax2.hist(data, bins=100, normed=True, cumulative=True) plt.show()
散布図
np.random.seed(0) x = np.random.choice(np.arange(100), 100) y = np.random.choice(np.arange(100), 100) # x, yと同じサイズのデータを用意する # zの値に応じてマーカーの大きさや濃さを変化させられる z = np.random.choice(np.arange(100), 100) fig = plt.figure(figsize=(15,5)) ax1 = fig.add_subplot(1, 2, 1) ax2 = fig.add_subplot(1, 2, 2) # zの値に応じてマーカーの大きさが変化 ax1.scatter(x, y, s=z) # zの値に応じて、マーカーの濃さが青系統で変化 sc = ax2.scatter(x, y, c=z, cmap="Blues") # カラーバーを表示 plt.colorbar(sc) plt.show()
円グラフ
data = [60, 20, 10, 5, 3, 2] labels = ["Apple", "Orange", "Banana", "Pineapple", "Kiwifruit", "Strawberry"] explode = [0, 0, 0.1, 0, 0, 0] fig = plt.figure(figsize=(15,5)) ax1 = fig.add_subplot(1, 2, 1) ax2 = fig.add_subplot(1, 2, 2) # axis('equal')がないと楕円になる ax1.pie(data, labels=labels, explode=explode) # labels付きのBananaを目立たせた円グラフ ax2.pie(data, labels=labels, explode=explode) ax2.axis("equal") plt.show()
3Dグラフ
from mpl_toolkits.mplot3d import Axes3D
以下では上記もインポート済みとする。
格子を作成する際に用いているnp.meshgridについては配列の要素から格子列を生成するnumpy.meshgrid関数の使い方 - DeepAgeが分かりやすかった。
- 3D描画機能を持ったサブプロット
t = np.linspace(-2*np.pi, 2*np.pi) X, Y = np.meshgrid(t, t) R = np.sqrt(X**2 + Y**2) Z = np.sin(R) fig = plt.figure(figsize=(6,6)) ax = fig.add_subplot(1, 1, 1, projection='3d') # プロットして表示 ax.plot_surface(X, Y, Z) plt.show()
- 3Dヒストグラム
fig = plt.figure(figsize=(5, 5)) ax1 = fig.add_subplot(111, projection="3d") # x, y, zの位置 xpos = [i for i in range(10)] ypos = [i for i in range(10)] zpos = np.zeros(10) # x, y, zの増加量 dx = np.ones(10) dy = np.ones(10) dz = [i for i in range(10)] # 3次元bar ax1.bar3d(xpos, ypos, zpos, dx, dy, dz) plt.show()
- 3D散布図
np.random.seed(0) X = np.random.randn(1000) Y = np.random.randn(1000) Z = np.random.randn(1000) fig = plt.figure(figsize=(6, 6)) ax = fig.add_subplot(1, 1, 1, projection="3d") # X,Y,Zを1次元に変換 # 今回のケースだともともと1次元だから必要ない x = np.ravel(X) y = np.ravel(Y) z = np.ravel(Z) # 3D散布図 ax.scatter3D(x, y, z) plt.show()
- 3Dグラフへのカラーマップ適用
- 色が単調な3Dグラフは凹凸が見えにくいことがある
- zの値についてカラーマップを適用する
# カラーマップを表示するためのライブラリ from matplotlib import cm t = np.linspace(-2*np.pi, 2*np.pi) X, Y = np.meshgrid(t, t) R = np.sqrt(X**2 + Y**2) Z = np.sin(R) fig = plt.figure(figsize=(6, 6)) ax = fig.add_subplot(1,1,1, projection="3d") ax.plot_surface(X, Y, Z, cmap=cm.coolwarm) plt.show()
なんか今までなんとなくでmatplotlib使ってたからここでやれてよかったかも。
【Aidemy】 ディープラーニングで手書き数字を識別してみよう①
Aidemyのディープラーニングで手書き数字を識別してみよう!に取り組んだので記録用に。
基本的にはやったことずらずら書き連ねただけ。
今回はコース1:Python入門〜コース2:Numpyを用いた数値計算をやった。
Python入門
Pythonの基礎。飛ばしてもよかったな、、、。
Numpy入門
- Pythonでベクトルや行列計算を高速に行うことができるライブラリ。
計算速度比較
# 実際に(150, 150)の行列の掛け算 Pythonの機能のみでの計算結果:3.75[sec] Numpyを使った場合の計算結果:0.00[sec]
スライス
arr2 = arr[:].copy()
num_list2 = num_list[:] # listならこれでOK
ブールインデックス参照
arr = [1, 2, 3, 4, 5, 6] print(arr % 2 == 0) #=> [False True False True False True] print(arr[arr % 2 == 0]) #=> [2, 4, 6]
集合関数
arr1 = [2, 5, 7, 9, 5, 2] arr2 = [2, 5, 8, 3, 1] # arr1の重複をなくす # new_arr1 = [2, 5, 7, 9] new_arr1 = np.unique(arr1) # new_arr1とarr2の和集合 # [1 2 3 5 7 8 9] np.union1d(new_arr1, arr2) # new_arr1とarr2の積集合 # [2 5] np.intersect1d(new_arr1, arr2) # new_arr1からarr2を引いた差集合 # [7 9] np.setdiff1d(arr1, arr2)
乱数
np.random.rand() # 一様乱数 np.random.randint(x, y, z) # x以上y未満のshapeがzの乱数 np.random.normal # ガウス分布
axis
- 列方向の処理がaxis=0、行方向の処理がaxis=1
arr = np.array([[1, 2, 3], [4, 5, 12], [15, 20, 22]]) arr.sum() #=> 84 arr.sum(axis=0) #=> [20 27 37] arr.sum(axis=1) #=> [6 21 57]
ファンシーインデックス参照
- インデックス参照にインデックスの配列を用いる方法
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) print(arr[[2, 0]]) # 出力結果(コピーを返す) [[7 8 9] [1 2 3]]
転地行列
np.transpose(arr)
ソート
arr = ([[4, 2, 5], [3, 1, 9]]) arr.argsort(arr) #=> [[2 1 0] [2 0 1]] arr.sort(arr) #=> [[2 4 8] [1 3 5]]
行列計算
# arr1とarr2の行列積 print(np.dot(arr1, arr2)) # vecのノルム print(np.linalg.norm(vec))
統計関数
np.mean() # 平均 np.sum() # 合計 np.max() # 最大値 np.min() # 最小値 np.argmax() # 最大値を取るインデックス np.argmin() # 最小値を取るインデックス np.std() # 標準偏差 np.var() # 分散
ブロードキャスト
- 2つのndarray同士の演算時にサイズの小さい配列の列もしくは行を自動で大きい配列に合わせる
- すべての配列で可能なわけではないが、すべての要素に同じ処理をするときなどはブロードキャスト可能