matplotlibでSurface plots

昨日に続いてmatplotlibの3次元グラフの話です。
今回のテーマは Surface plots。(日本語では表面プロットでいいのかな?)
2変数関数の可視化等に便利なやつですね。

ドキュメントは今回もこちら。 : The mplot3d Toolkit

今回は例として 鞍点を持つ次の関数を可視化してみましょう。
$$
z = f(x, y) = x^2 – y^2.
$$

比較用に等高線で可視化したグラフと並べてみました。
参考: matplotlibで等高線


import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np


def f(x, y):
    return x**2 - y**2


X, Y = np.meshgrid(
        np.linspace(-10, 10, 101),
        np.linspace(-10, 10, 101),
    )
Z = f(X, Y)

fig = plt.figure(figsize=(16, 6), facecolor="w")
ax_3d = fig.add_subplot(121, projection="3d")
ax_3d.plot_surface(X, Y, Z)

ax = fig.add_subplot(122)
contour = ax.contourf(X, Y, Z)
fig.colorbar(contour)
plt.show()

結果はこちら。

可視化する対象によって向き不向きがあるのでいつもそうだというわけではないのですが、
今回のサンプルでは圧倒的に3次元プロットの方が圧倒的に関数の形をつかみやすいですね。

matplotlibで3D散布図

matplotlibで3次元のグラフを作成する方法のメモです。
今回は散布図を描いてみます。

matplotlibで3次元のグラフを書くには、mplot3d Toolkitというのを使います。
ドキュメント: The mplot3d Toolkit
また、 3次元散布図についてはこちらのドキュメントも参考になります。 3D scatterplot

ポイントとしては、(importした後明示的には使わないので忘れがちですが、)
Axes3Dを必ずインポートしておくことと、axを取得するときに、
projection="3d"を忘れないことですね。

iris のデータの4つの特徴量の中から適当に3個選んでやってみます。


import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from sklearn.datasets import load_iris

iris = load_iris()
data = iris.data
label = iris.target

fig = plt.figure(figsize=(8, 8), facecolor="w")
ax = fig.add_subplot(111, projection="3d")
for c in range(3):
    ax.scatter(
        data[label == c, 0],
        data[label == c, 2],
        data[label == c, 3],
        label=iris.target_names[c]
    )

ax.set_xlabel(iris.feature_names[0])
ax.set_ylabel(iris.feature_names[2])
ax.set_zlabel(iris.feature_names[3])

ax.legend()
plt.show()

結果がこちら。
綺麗に3次元のプロットができました。

pandasのSeriesを辞書型に変換する3つの方法

以前書いた、 DataFrameの2列の値からdictを作る に近い話です。
今回の対象は Dataframeではなく Series。
元々、辞書と同じようにSeries[kye]で、値を取り出すことができるので、
DataFrameに比べて辞書型に変換するニーズも少ないと思うのですが、
to_dict() メソッドを持ってることを最近知ったのでこの記事を書きました。

まず、サンプルルとなるデータを作ります。


import pandas as pd
data = pd.Series({chr(i): i for i in range(97, 105)})
print(data)
"""
a     97
b     98
c     99
d    100
e    101
f    102
g    103
h    104
dtype: int64
"""

それでは、これを3種類の方法で辞書に変換してみましょう。
それぞれ、内包表記を使う方法(昔よく使っていた。)、 to_dict()を使う方法、 dict()にいれてしまう方法(一番楽)です。


print({k: v for k, v in chr_codes.items()})
# {'a': 97, 'b': 98, 'c': 99, 'd': 100, 'e': 101, 'f': 102, 'g': 103, 'h': 104}

print(chr_codes.to_dict())
# {'a': 97, 'b': 98, 'c': 99, 'd': 100, 'e': 101, 'f': 102, 'g': 103, 'h': 104}

print(dict(chr_codes))
# {'a': 97, 'b': 98, 'c': 99, 'd': 100, 'e': 101, 'f': 102, 'g': 103, 'h': 104}

dictに変換するメリットとして、dictならgetメソッドが使えて、keyの中に取りたい値がない場合のデフォルト値の設定もできますよっていう話を
最後に書こうと思っていたのですが、
Seriesにも getメソッドは用意されていて、ほぼ同じように動作するようでした。
pandas.Series.get

どちらかというと、辞書に変換する方法よりも辞書に変換しなくても
同じように使えるってことを覚えておいた方が有益に思います。

matplotlibで二重軸

matplotlibで一つの枠の中に複数のグラフを書く場合、値のレンジが近ければ良いのですが、
10倍以上も違うと片方のグラフが潰れてしまうなど、不便なことがあります。

そのような時はだいたいグラフを分けて書いたり、
Tableauなどの別のソフトを使って2重軸のグラフを書くなどの対応をしているのですが、
matplotlibでも左右の軸を使ったグラフを書くことはできます。

その際は、 twinx というメソッドを使います。
以下、サンプルコードです。
この時、凡例をつけておかないと、それぞれのグラフがどちらの軸を見るのかわからないので、つけるのですが、
ちょっとつけかたが特殊なので、そのサンプルも兼ねています。
(get_legend_handles_labels というメソッドを使います。)


import matplotlib.pyplot as plt
import numpy as np

# データの作成
x = np.arange(0, 20)
y1 = np.random.randint(300, 500, size=20)
y2 = np.random.randint(0, 20, size=20)

fig = plt.figure(facecolor="w")
ax1 = fig.add_subplot(1, 1, 1)
ax1.plot(x, y1, label="折れ線グラフ")
ax1.set_ylabel("折れ線グラフ")
ax1.set_ylim([200, 500])

# 2重軸の作成
ax2 = ax1.twinx()
ax2.bar(x, y2, label="棒グラフ", color="g", alpha=0.5)
ax2.set_ylabel("棒グラフ")
ax2.set_ylim([0, 60])

# 凡例をまとめて出力する
handler1, label1 = ax1.get_legend_handles_labels()
handler2, label2 = ax2.get_legend_handles_labels()
ax1.legend(handler1 + handler2, label1 + label2)

plt.show()

出力される図がこちら。

層化K分割交差検証の紹介とPythonで実行する方法

少し前の記事になりますが、 scikit-learnでK-分割交差検証 というのを書きました。
これは、分類のタスクでは目的変数の件数がクラスごとにある程度揃っていたり、データが十分に揃っていればうまく機能します。
しかし、一方で不均衡データなど、目的変数の値の割合が偏っていて特に、一部のクラスのデータが非常に少ないと困ったことになります。

試しに、いつものirisのデータを少し絞り込んで、元々種類ごとに50件ずつあるデータを
setosa: 50個
versicolor: 10個
virginica: 5個
にして試してみます。
(一番少ないクラスのデータ件数が5個なのに5分割するという極端な例ですが、
説明のためなのでご了承ください。)


from sklearn.model_selection import KFold
from sklearn.datasets import load_iris
import numpy as np

# データの読み込み
iris = load_iris()
X = iris.data
y = iris.target

# 実験のため対象を絞り込んで不均衡データにする
index = list(range(50)) + list(range(50, 60)) + list(range(100, 105))
X = X[index]
y = y[index]

for c in range(3):
    print(f"{iris.target_names[c]}: {list(y).count(c)}個")

"""
setosa: 50個
versicolor: 10個
virginica: 5個
"""

# KFoldを用いてK-分割交差検証した時に各グループに含まれるラベル数
kf = KFold(5, shuffle=True)
i = 0
for train_index, test_index in kf.split(X):
    i += 1
    print(f"\n{i}グループの訓練データに含まれるラベル")
    train_y = y[train_index]
    for c in range(3):
        print(f"{iris.target_names[c]}: {list(train_y).count(c)}個")
    print(f"{i}グループのテストデータに含まれるラベル")
    test_y = y[test_index]
    for c in range(3):
        print(f"{iris.target_names[c]}: {list(test_y).count(c)}個")

"""
1グループの訓練データに含まれるラベル
setosa: 41個
versicolor: 6個
virginica: 5個
1グループのテストデータに含まれるラベル
setosa: 9個
versicolor: 4個
virginica: 0個

2グループの訓練データに含まれるラベル
setosa: 42個
versicolor: 7個
virginica: 3個
2グループのテストデータに含まれるラベル
setosa: 8個
versicolor: 3個
virginica: 2個

3グループの訓練データに含まれるラベル
setosa: 39個
versicolor: 9個
virginica: 4個
3グループのテストデータに含まれるラベル
setosa: 11個
versicolor: 1個
virginica: 1個

4グループの訓練データに含まれるラベル
setosa: 40個
versicolor: 8個
virginica: 4個
4グループのテストデータに含まれるラベル
setosa: 10個
versicolor: 2個
virginica: 1個

5グループの訓練データに含まれるラベル
setosa: 38個
versicolor: 10個
virginica: 4個
5グループのテストデータに含まれるラベル
setosa: 12個
versicolor: 0個
virginica: 1個
"""

結果が長くなって恐縮ですが、1グループ目では、テストデータにvirginicaが含まれなくなっていますし、
5グループ目では versicolor がテストデータにありません。
逆に、訓練データでそれらのデータの割合が過剰に高くなっています。
これではモデルの学習もうまくいきませんし、評価も適切に行えません。

このような時、 train_test_splitであれば、stratify引数を使って、ラベルの割合を揃えられます。
参考: scikit-learnのtrain_test_splitで、訓練データとテストデータのラベルの割合を揃える
そして、KFoldには stratify がないのですが代わりに、
タイトルの 層化K分割交差検証(Stratified K-Folds cross-validator)という手法が知られており、それに対応する
StratifiedKFold というクラスが用意されています。

要は、ラベルの割合を揃えながらK分割交差検証する方法です。
使い方はKFoldととても似ていますが、splitするときに、labelも渡してあげる必要がある点だけ注意です。


from sklearn.model_selection import StratifiedKFold

skf = StratifiedKFold(5, shuffle=True)
i = 0
for train_index, test_index in skf.split(X, y):
    i += 1
    print(f"\n{i}グループの訓練データに含まれるラベル")
    train_y = y[train_index]
    for c in range(3):
        print(f"{iris.target_names[c]}: {list(train_y).count(c)}個")
    print(f"{i}グループのテストデータに含まれるラベル")
    test_y = y[test_index]
    for c in range(3):
        print(f"{iris.target_names[c]}: {list(test_y).count(c)}個")

"""
1グループの訓練データに含まれるラベル
setosa: 40個
versicolor: 8個
virginica: 4個
1グループのテストデータに含まれるラベル
setosa: 10個
versicolor: 2個
virginica: 1個

2グループの訓練データに含まれるラベル
setosa: 40個
versicolor: 8個
virginica: 4個
2グループのテストデータに含まれるラベル
setosa: 10個
versicolor: 2個
virginica: 1個

3グループの訓練データに含まれるラベル
setosa: 40個
versicolor: 8個
virginica: 4個
3グループのテストデータに含まれるラベル
setosa: 10個
versicolor: 2個
virginica: 1個

4グループの訓練データに含まれるラベル
setosa: 40個
versicolor: 8個
virginica: 4個
4グループのテストデータに含まれるラベル
setosa: 10個
versicolor: 2個
virginica: 1個

5グループの訓練データに含まれるラベル
setosa: 40個
versicolor: 8個
virginica: 4個
5グループのテストデータに含まれるラベル
setosa: 10個
versicolor: 2個
virginica: 1個
"""

全グループで、訓練データとテストデータの割合が揃っているのを確認できました。

評価値の改善が止まった時に学習率を減らす

今回もkerasの学習率改善のコールバックの話です。
LearningRateScheduler を使って、エポックごとの学習率を変えられることを紹介しましたが、
実際、学習をやってみる前に最適な学習率の変化の計画を立てておくことは非常に困難です。
最初は大きめの値でどんどん学習して、それではうまくいかなくなった段階で徐々に下げるということをやりたくなります。

そして、 kerasにはそのためのコールバックの、ReduceLROnPlateau というのが用意されています。
監視する評価値、何エポック改善しなかったら学習率を落とすか、その変化の割合、最小値などを指定すると、
学習の進みに応じて調整してくれます。

さっそく適当なモデルで試してみましょう。
(今回は着目するのが学習率の変化なので、下のコードのモデルは対して良いものでもないことをご了承ください。)


from tensorflow.keras.datasets import fashion_mnist
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import Dropout
from tensorflow.keras.layers import Flatten
from tensorflow.keras.layers import Conv2D
from tensorflow.keras.layers import MaxPooling2D
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import EarlyStopping
from tensorflow.keras.callbacks import ReduceLROnPlateau
import matplotlib.pyplot as plt
from sklearn.metrics import classification_report

# データの読み込み
(data_train, target_train), (data_test, target_test) = fashion_mnist.load_data()

# Conv2D の inputに合わせて変形
X_train = data_train.reshape(-1, 28, 28, 1)
X_test = data_test.reshape(-1, 28, 28, 1)

# 特徴量を0~1に正規化する
X_train = X_train / 255
X_test = X_test / 255

# ラベルを1 hot 表現に変換
y_train = to_categorical(target_train, 10)
y_test = to_categorical(target_test, 10)

# lr に少し大きめの値を設定しておく (デフォルトは lr =0.001)
adam = Adam(lr=0.01)

# モデルの構築
model = Sequential()
model.add(Conv2D(16, kernel_size=(3, 3),
                 activation='relu',
                 input_shape=(28, 28, 1)))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(32, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
model.add(Dense(64, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))
model.compile(
    loss="categorical_crossentropy",
    optimizer=adam,
    metrics=['acc']
)
print(model.summary())
"""
Model: "sequential_3"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_6 (Conv2D)            (None, 26, 26, 16)        160       
_________________________________________________________________
max_pooling2d_6 (MaxPooling2 (None, 13, 13, 16)        0         
_________________________________________________________________
conv2d_7 (Conv2D)            (None, 11, 11, 32)        4640      
_________________________________________________________________
max_pooling2d_7 (MaxPooling2 (None, 5, 5, 32)          0         
_________________________________________________________________
flatten_3 (Flatten)          (None, 800)               0         
_________________________________________________________________
dense_6 (Dense)              (None, 64)                51264     
_________________________________________________________________
dropout_7 (Dropout)          (None, 64)                0         
_________________________________________________________________
dense_7 (Dense)              (None, 10)                650       
=================================================================
Total params: 56,714
Trainable params: 56,714
Non-trainable params: 0
_________________________________________________________________
"""
early_stopping = EarlyStopping(
                        monitor='val_loss',
                        min_delta=0.0,
                        patience=10,
                )

# val_lossの改善が2エポック見られなかったら、学習率を0.5倍する。
reduce_lr = ReduceLROnPlateau(
                        monitor='val_loss',
                        factor=0.5,
                        patience=2,
                        min_lr=0.0001
                )

history = model.fit(X_train, y_train,
                    batch_size=128,
                    epochs=50,
                    verbose=2,
                    validation_data=(X_test, y_test),
                    callbacks=[early_stopping, reduce_lr],
                    )
"""
   (途中は省略。以下は最終的な結果)
Epoch 26/50
60000/60000 - 12s - loss: 0.2263 - acc: 0.9138 - val_loss: 0.3177 - val_acc: 0.8998
"""

さて、学習が完了したことで、history に結果が入りましたので、 監視していた val_loss と学習率 lrをみてみましょう。


# val_loss と lr を可視化
fig = plt.figure(figsize=(10, 10), facecolor="w")
ax = fig.add_subplot(2, 1, 1)
ax.set_title("val_loss")
ax.plot(range(len(history.history["val_loss"])), history.history["val_loss"])
ax = fig.add_subplot(2, 1, 2)
ax.set_title("lr")
ax.plot(range(len(history.history["lr"])), history.history["lr"])
plt.show()

学習率が段階的に半減していっているのが確認できますね・

kerasの学習率調整の結果をhistoryオブジェクトから確認する

昨日の記事に続いて、学習率調整の話です。
参考: kerasのLearningRateSchedulerで学習途中に学習率を調整する

昨日の記事では、動作確認のため、LearningRateSchedulerの引数、verboseに1を設定して、
エポックごとの学習率を出力して動きを見ていました。
しかし、見ての通り、出力がかなり煩雑になって、なかなか煩わしいです。
なので、普段は verbose=0 で使うのですが、そうすると、思うように学習が進まなかった時に、
調査の一環で学習率の変化の具合を見ようと思うと困っていました。
(verboseを1になおして再実行すると時間がかかるし、結果も変わるので。)

その時、何気なくhistoryオブジェクト(fit関数の戻り値)を可視化してみると、その中に lr も含まれているのを見つけました。
昨日の記事のコードの続きで実行してみるとこんな感じで。


print(history.history["lr"])
# [0.002, 0.002, 0.002, 0.001, 0.001, 0.0005, 0.0005, 0.0005, 0.0005, 0.0005]

history.history には, accもlossも入っているので、学習率の変化やその影響の確認は,
これをみるのが一番良さそうです。

kerasのLearningRateSchedulerで学習途中に学習率を調整する

機械学習や深層学習において、より効率的にモデルを学習させるテクニックとして、
学習の進捗に応じて、学習率を変更するというものがあります。

kerasでは、これを手軽に実行するために LearningRateScheduler というコールバックが用意されています。
これに、 エポックのインデックスを受け取って学習率を返す関数を渡して、
それをcallbackに設定しておくと実現できます。

だいたい次のイメージで使えます。
例によって、モデルはすでに構築されているものとします。
(下のコードで動かしてるモデルはこのブログの CNNで手書き数字文字の分類 の記事からコピーして持ってきました。)


# 学習率を返す関数を用意する
def lr_schedul(epoch):
    x = 0.002
    if epoch >= 3:
        x = 0.001
    if epoch >= 5:
        x = 0.0005
    return x


lr_decay = LearningRateScheduler(
    lr_schedul,
    # verbose=1で、更新メッセージ表示。0の場合は表示しない
    verbose=1,
)

history = model.fit(X_train, y_train,
                    batch_size=128,
                    epochs=10,
                    verbose=2,
                    validation_data=(X_test, y_test),
                    callbacks=[lr_decay],
                    )

# 以下出力
"""
Train on 60000 samples, validate on 10000 samples
Epoch 1/10

Epoch 00001: LearningRateScheduler setting learning rate to 0.002.
 - 13s - loss: 0.4354 - acc: 0.8625 - val_loss: 0.0707 - val_acc: 0.9762
Epoch 2/10

Epoch 00002: LearningRateScheduler setting learning rate to 0.002.
 - 12s - loss: 0.1748 - acc: 0.9476 - val_loss: 0.0534 - val_acc: 0.9823
Epoch 3/10

Epoch 00003: LearningRateScheduler setting learning rate to 0.002.
 - 12s - loss: 0.1376 - acc: 0.9590 - val_loss: 0.0387 - val_acc: 0.9872
Epoch 4/10

Epoch 00004: LearningRateScheduler setting learning rate to 0.001.
 - 14s - loss: 0.1105 - acc: 0.9675 - val_loss: 0.0332 - val_acc: 0.9882
Epoch 5/10

Epoch 00005: LearningRateScheduler setting learning rate to 0.001.
 - 15s - loss: 0.1041 - acc: 0.9694 - val_loss: 0.0311 - val_acc: 0.9902
Epoch 6/10

Epoch 00006: LearningRateScheduler setting learning rate to 0.0005.
 - 15s - loss: 0.0960 - acc: 0.9725 - val_loss: 0.0293 - val_acc: 0.9899
Epoch 7/10

Epoch 00007: LearningRateScheduler setting learning rate to 0.0005.
 - 14s - loss: 0.0889 - acc: 0.9735 - val_loss: 0.0275 - val_acc: 0.9899
Epoch 8/10

Epoch 00008: LearningRateScheduler setting learning rate to 0.0005.
 - 18s - loss: 0.0880 - acc: 0.9747 - val_loss: 0.0273 - val_acc: 0.9899
Epoch 9/10

Epoch 00009: LearningRateScheduler setting learning rate to 0.0005.
 - 14s - loss: 0.0856 - acc: 0.9746 - val_loss: 0.0274 - val_acc: 0.9905
Epoch 10/10

Epoch 00010: LearningRateScheduler setting learning rate to 0.0005.
 - 13s - loss: 0.0811 - acc: 0.9764 - val_loss: 0.0264 - val_acc: 0.9906
"""

今回はお試しで学習率が変わっていることを見たかったので、 verbose=1 を指定して、LearningRateSchedulerにも
ログを出力させました。
最初の 0.002 から、 0.0005へと、学習率が変わっていっていることがわかります。

ただ、少し煩わしいので、普段の利用では verbose=0 (デフォルトなので未指定でも可)がおすすめです。

ImageDataGeneratorで拡張したデータでモデルを学習する

ImageDataGenerator で画像データを拡張(水増し)したら、それを使って機械学習のモデルを学習したいと思います。

一番単純なのは、欲しいデータ量を決めて、flowメソッドで必要なだけデータを生成し、
それを配列に格納しておいてがくしゅうする方法だと思います。

ただ、これを実行すると、生成したデータ量にもよりますがすぐメモリの消費量が増えてメモリエラー等が発生します。
このような時、拡張された具体的なデータではなく、ジェネレーターをそのまま渡して学習するメソッドがkerasには用意されています。

それが、モデルクラスの fit_generator です。

それぞれのメソッドの引数をみてみましょう。


fit(
    x=None,
    y=None,
    batch_size=None,
    epochs=1,
    verbose=1,
    callbacks=None,
    validation_split=0.0,
    validation_data=None,
    shuffle=True,
    class_weight=None,
    sample_weight=None,
    initial_epoch=0,
    steps_per_epoch=None,
    validation_steps=None
)

fit_generator(
    generator,
    steps_per_epoch=None,
    epochs=1,
    verbose=1,
    callbacks=None,
    validation_data=None,
    validation_steps=None,
    class_weight=None,
    max_queue_size=10,
    workers=1,
    use_multiprocessing=False,
    shuffle=True,
    initial_epoch=0
)

fitでは、最初のふたつの引数x,yでデータとラベルを渡しますが、fit_generatorではそれがgeneratorになります。
そして、このgeneratorが、バッチサイズ分のデータとラベルを一式生成し続けてそれを使って学習します。

次のようなコードで、利用することができます。
(例ではvalidation_dataは固定にしましたが、ここでもジェネレーターを使えます。)


from tensorflow.keras.preprocessing.image import ImageDataGenerator
datagen = ImageDataGenerator(
    # 設定は適当
    rotation_range=30,
    width_shift_range=0.2,
    height_shift_range=0.2,
    zoom_range = 0.2,
    channel_shift_range = 0.2,
    horizontal_flip=True,
)
datagen.fit(x_tr)

# モデル(model)と、データ(x_train,y_train)は別途準備されているものとします。
history = model.fit_generator(
            datagen.flow(x_train, y_train, batch_size=256),
            epochs=100,
            verbose=2,
            validation_data=(x_val, y_val),
        )

ImageDataGeneratorの引数

前々回の記事: kerasによる画像データ拡張 で紹介した、
ImageDataGenerator を使って、色々画像を変換してみます。
引数の一覧はドキュメントにある通りで、
平行移動や回転、色の変更や拡大など色々行えます。
同時に複数を設定することももちろんできるのですが、今回は個々の動作をみたいので、1個ずつ適用しました。
また、どれもランダムに変換するものなので1枚の画像を5回ずつ変換して出力しています。

次が書いてみたコードです。結果がわかりやすいように変数はどれも大きめの値を渡しました。


from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.datasets import cifar10
import matplotlib.pyplot as plt

# データ取得
(x_train, y_train), (x_test, y_test) = cifar10.load_data()

# 1データだけ抽出。
data = x_train[91: 92] / 255.
label = y_train[91: 92]

parameters = [
    # 整数.画像をランダムに回転する回転範囲
    {"rotation_range": 90},
    # 浮動小数点数(横幅に対する割合).ランダムに水平シフトする範囲
    {"width_shift_range": 0.5},
    # 浮動小数点数(縦幅に対する割合).ランダムに垂直シフトする範囲
    {"height_shift_range": 0.5},
    # 浮動小数点数.シアー強度(反時計回りのシアー角度)
    {"shear_range": 90},
    # 浮動小数点数または[lower,upper].ランダムにズームする範囲.
    # 浮動小数点数が与えられた場合,[lower, upper] = [1-zoom_range, 1+zoom_range]
    {"zoom_range": 0.5},
    # 浮動小数点数.ランダムにチャンネルをシフトする範囲
    {"channel_shift_range": 0.5},
    # 真理値.水平方向に入力をランダムに反転します
    {"horizontal_flip": True},
    # 真理値.垂直方向に入力をランダムに反転します
    {"vertical_flip": True},
]

fig = plt.figure(figsize=(11, 20))
for j, kwargs in enumerate(parameters):
    datagen = ImageDataGenerator(
                            **kwargs
                        )
    # 画像の基本的な統計量を学習する
    datagen.fit(data)

    for i in range(5):
        generated_data, generated_label = datagen.flow(data, label).next()

        ax = fig.add_subplot(len(parameters), 5, i+j*5+1)
        ax.set_xticklabels([])
        ax.set_yticklabels([])
        ax.imshow(generated_data[0])
        if i == 0:
            ax.set_title(kwargs)

plt.show()

結果がこちら。
猫の画像が変換されています。

平行移動や回転した時に、元々のと比べて隙間が発生してしまいますが、
それは、 fill_mode: {“constant”, “nearest”, “reflect”, “wrap”} で指定した方法で保管されます。
(デフォルトは ‘nearest’)

ドキュメントには下記のように記載があります。
“constant”: kkkkkkkk|abcd|kkkkkkkk (cval=k)
“nearest”: aaaaaaaa|abcd|dddddddd
“reflect”: abcddcba|abcd|dcbaabcd
“wrap”: abcdabcd|abcd|abcdabcd

constant は cval で指定した定数。
nearest は境界部分の色を引き延ばした形。
reflect は境界を境に線対称。
wrap は反対側からループするようなイメージです。