アプリとサービスのすすめ

アプリやIT系のサービスを中心に書いていきます。たまに副業やビジネス関係の情報なども気ままにつづります

kerasでLSTM(QRNN)を使って異常検知手法で急上昇ワードをやってみた(機械学習-変化点検知)

急上昇ワード(バースト検知)とえば、googleyahoo!でも似たようのがある。今回はあれほど高性能じゃないけど、急上昇ワードと同じ仕組みのものを異常検知手法でやってみた。

目次
・訓練
閾値の設定
・テスト
・実際に急上昇ワードをやってみる

今回はBigQuery(BQ)から抜き出した、一定期間内の検索ワードの急上昇を検知してみる。みんな大好きニューラルネットワークのLSTMで訓練し、バースト検知に利用した。訓練から、実際にBQから取り出した検索ワードの急上昇検知実行までの流れを一通り書いて行く。




訓練


まず訓練データ、「mizugi」と「bts」というワードを2つ、8ヶ月分ほど抽出。btsは2桁(10台)の検索数が多く、mizugiは検索数が多く、3桁(100〜1000)のワードだ。
まずLSTMでbtsを8ヶ月分くらい、急上昇部分があっても構わず学習させた。


btsとmizugiの急上昇部分は特異変換スペクトルで検出した。結果がこれ

bts
f:id:trafalbad:20180826171931p:plain

mizugi
f:id:trafalbad:20180826171944p:plain

アルゴリズムはLSTMを使った。QRNNとかいう発展版も試したが、LSTMの方がkerasで簡単に使えるし、十分なので途中で断念。

データをLSTMで使えるようにする正規化とかの前処理と、モデル作成はこんな感じ

def get_data(data, time_steps):
    docX, docY = [], []
    for i in range(len(data)-time_steps):
        docX.append(data[i:i+time_steps])
        docY.append(data[i+time_steps])
    alsX = np.array(docX)
    alsY = np.array(docY)
    return alsX, alsY

def transform_data(data, inverse_option, scaler):
    data_shape = data.shape
    if inverse_option is True:
        data = scaler.inverse_transform(data)
    else:
        data = scaler.fit_transform(data)
    data = data.reshape(data_shape)
    return data, scaler

def prepare_data(original_data, time_steps):
    copy_data = original_data.copy()
    scaler = MinMaxScaler(feature_range=(0, 1), copy=False)
    data, scaler = transform_data(data=copy_data, 
                              inverse_option=False, scaler=scaler)
    
    x,y = get_data(data, time_steps=time_steps)
    return x, y, scaler

time_steps = 12

x, y, scaler = prepare_data(X_train, time_steps)

input_dim=x.shape[-1]
timesteps=x.shape[1]

model = Sequential()
model.add(LSTM(300, input_shape=(timesteps, input_dim),
         stateful=False,return_sequences=True))
model.add(Flatten())
model.add(Dense(1, kernel_initializer='lecun_uniform'))
model.add(Activation("linear"))
# model.load_weights('/Users/d/model.ep200.h5')
model.compile(loss="mean_squared_error", optimizer="adam",)

以下が訓練後、btsの学習期間を予測した図。赤がリアルの検索数値で青が予測値だ。btsは2桁の値なのか知らないけど、損失関数はあまり減少しなかった。学習結果としてはいまいちだったけど、それなりによく予測できてたかな。

btsの予測
f:id:trafalbad:20180826172015p:plain


閾値の設定


ここからbtsとmizugiから以下のように検証・テスト1・テスト2の3つにデータを分けた

・検証データ→btsの急上昇のない部分(異常なしデータ)

・テストデータ1→btsの急上昇のある部分(異常ありデータ)

・テストデータ2→mizugiの急上昇のある部分(異常ありデータ)


バースト検知は異常検知の中で「変化点検出」に当たる。

それには少し変わった閾値設定をする必要がある。以下の流れで閾値を設定した。

①検証データはオリジナルの閾値の設定に使用

②検知したいワードの平均値をオリジナルの閾値にかけて、その検知したいワードに最適な閾値を再設定する


変化点検知の閾値設定には、回帰(予測)モデルと、mseの組み合わせから求めるのが、結構メジャーらしい(参考記事)。




①検証データはオリジナルの閾値の設定に使用

今回の閾値の設定には、検証データを使って最初の(オリジナルの)閾値を求めた。コードは以下の通り。

def calculate_mse(value, predict_value, variance=1.0):
    value = value[:, 0]
    predict_value = predict_value[:, 0]
    mse_value = [(v - p_v)**2 for v, p_v in zip(value, predict_value)]
    return np.array(mse_value)


mse_value_valid = calculate_mse(x_scale_valid, predict_valid)
threshold = np.max(mse_value_valid)
# threshold:249.16269761799867

検証データ(異常なしデータ)の全期間から、MSEを求めて、その最大値を閾値とした(つまり、検証データの異常値の最大値)。




②検知したいワードの平均値をオリジナルの閾値にかけて、その検知したいワードに最適な閾値を再設定する


ここで問題なのは。btsは2桁の値なので閾値はかなり低い。これを水着のような3桁、4桁以上の検索数のワードに適用すると閾値が小さすぎて、全部の異常値を検出してしまう。


そこで検知するワードに対して最適な閾値を再設定してやる必要がある。以下のやり方で設定した。


1.検証データから求めたMSEの最大値をオリジナルの閾値とする

2.検知したいワードの全期間の検索数の平均値割る2の値を四捨五入して整数にする

3.それをオリジナルの閾値にかけて、検知したいワードの閾値(high_threshold)として再設定

わかりにくいので、コードで表すとこんな感じ

mse_value_normal = calculate_mse(x_scale_train, predict_train)
N=int(np.mean(x_scale_train)/2)
high_threshold = threshold*N 

なんで2で割ったのかは、いろいろ試して一番検出に適した値になるからなので、別に整数(int)じゃなくて、小数(float)のままでもよかった。





テスト


再設定した閾値で、テストデータ1、2のバースト(異常)を上手く検知できてるか試す。

上からテストデータ1(bts)、テストデータ2(mizugi)の時系列データと、再設定した閾値をプロットした

bts
f:id:trafalbad:20180826172037p:plain

mizugi
f:id:trafalbad:20180826172050p:plain

元の閾値よりも高い閾値で、程よく急上昇部分が検知されてる。一日、1週間、一月の期間でも試したが、上手く検出できてた。





実際に急上昇ワードをやってみる


いよいよ、バースト検知を試してみる。今回はjupyter上から直接、検索ワードをBigQueryから抽出して、検出してみた。


急上昇検知のメイン部分はこんな感じ

csvs=[]
original_threshold = 270.2200 
train_data_mean=4.964
num_words=len(words)
for i in range(num_words):
    cnt=pd.DataFrame(list(cnt_listed[i][0])).dropna()
    hour=pd.DataFrame(list(hour_listed[i][0])).dropna()
    hours=np.array(hour[window:])
    wo=cnt_listed[i][1] # each word
    
    x_predict, y_ ,scaler = prepare_data(cnt, time_steps)
    predicted, x_scale, mse_value = predict_model_show_graph(hour[window:], x_predict, y_, scaler, model, time_steps)
    mse_value = calculate_mse(x_scale, predicted)
    
    N=int(np.mean(x_scale)/2)
    high_threshold = original_threshold*N  # 閾値
    if high_threshold<original_threshold:
        #print('high_threshold: {}'.format(original_threshold))
        #show_graph_threshold(hour[window:], mse_value, original_threshold, 'Anomaly Score test', "r")
        # 急上昇ワードの時間を表示
        for mse, hour in zip(mse_value, hours):
            if mse >=original_threshold:
                csvs.append([hour, wo])
    else:
        #print('high_threshold: {}'.format(high_threshold))
        #show_graph_threshold(hour[window:], mse_value, high_threshold, 'Anomaly Score test', "r")
        # 急上昇ワードの時間を表示
        for mse, hour in zip(mse_value, hours):
            if mse >=high_threshold:
                csvs.append([hour, wo])

その前の前処理では、取り出したワードを時系列データに変換したり、LSTM用に正規化したりした。

今回は3月3日の分だけ、検索ワードを取り出して、検知。図をplotとするといい具合に検出できてるっぽい。

最終的には、ワードと時間を抽出する形にした。


閾値の設定は前に書いた動的時間伸縮法(DTW)でもできるけど、計算量がバカにならない。なので、MSEとかのローコストな計算式を臨機応変に使い分けるのが最適だと思う




今回はLSTMを用いた、変化点検出手法で急上昇ワードのバースト検知をやってみた。
昔はchange finderとかsmartfilterとか、ゴツい検出アルゴリズムがあったけど、ニューラルネットワークの登場で異常検知もかなりやりやすく、精度も高くなった。

異常行動検知もLSTMを使ってできる。機械学習シリーズにある密度比推定とか非構造学習みたなガチもんの専門的な内容には、機会があったら触れてみたい。

追記:QRNNで可視化してみた
LSTMより精度のいいらしいQRNNで同様に、訓練・可視化してみた。この参考サイトではQRNNのコードの一部を修正してるが、2019/2月時点ではgithubのはもう修正済みだったので、修正の必要なかった。

# git clone してpythonファイルをコピー
$ git clone https://github.com/DingKe/nn_playground && cd nn_playground/qrnn 

$ cp /Users/desktop/nn_playground/qrnn/qrnn.py /Users/desktop/qrnn.py
from qrnn import QRNN

#省略
print(x.shape) # (6354, 12, 1)
print(y.shape) # (6354, 1)

"""
モデルの作成
自身の状態をリセットするかしないかを指定するstateful(周期的な波形はTrueの方が若干良い)
"""
input_layer = Input(shape=(12, 1))
qrnn_output_layer = QRNN(64, window_size=12, dropout=0)(input_layer)
prediction_result = Dense(1)(qrnn_output_layer)
model = Model(input=input_layer, output=prediction_result)
model.compile(loss="mean_squared_error", optimizer="Adam")

# 訓練( batch_size:指定しなくてもいい)
history = model.fit(x, y, epochs=200, batch_size=500)

btsの予測 by LSTM
f:id:trafalbad:20180826172015p:plain


btsの予測 by QRNN
f:id:trafalbad:20190223144258p:plain


LSTMの予測結果と比べると若干いいのでは?くらいだと思う。条件が単純すぎるから、もっと未来を予測とか複雑な条件であれば、はっきり精度の差は出ると思う。

列名とかデータ加工に使うpandasの便利な機能まとめ (python・機械学習)

今回はデータ加工に使えるpandasの機能を紹介する。kaggleを含め、機械学習のデータ加工はpandasでの加工が多い。

理由は単純にpandasはデータ加工において、扱いやすいから。今回はxgboostの特徴量を加工する機会があった。そのときに使ったり、調査したkaggleで人気なpandasのメソッドの中で使えそうなやつをまとめた。


機能1〜5まではここではirisデータセットで試した。

iris = datasets.load_iris()
df_feature_iris = pd.DataFrame(iris['data'],columns=['sepal_length','sepal_width','petal_length','petal_width'])
df_species_iris = pd.DataFrame(iris['target'],columns=['species'])
df=df_feature_iris
df.head()

f:id:trafalbad:20180816122129p:plain

用語について

pandasで使う用語で
・行
・列

がある。pandasは行列のmatrix形式で扱ので、ググればすぐわかるが、
行=>縦
列=>横
と考えると分かりやすい

行が横方向にならんだものを示し、列が縦方向にならんだものを示す。表計算ソフトでも、横方向を行と言い、縦方向を列と言う(参考Wikipedia


pandas機能(1~19)


機能1
==行の複数条件指定==

これは列の内容で2つ以上の条件を指定する方法。

・純粋に&で指定するもの
SQLのクエリのように指定する方法
の2つがある。クエリライクのやり方はkaggleで人気

df[(df['sepal_length']>=7)&(df['sepal_width']>=3)]

#またはクエリのように指定する方法
df.query("sepal_length>=7 and sepal_width>=3")


機能2
==特定の型の列のみ取得==

列方向で特定のデータ型の値を取得する
pandasの場合、データ型は「整数('int')・小数('float')・真偽値('bool')」以外は全て「’object'」で指定

df.select_dtypes(['float']).head()


機能3
==列Aごとに列Bを集計する==

列Aのグループを作り、その値内で、列Bの値の数を集計する。

→irisデータで試す
'sepal_length'の列でグループを作り(4.3〜7.9)、'sepal_width'の値(2.0~4.4)を
値ごとに集計

df.groupby('sepal_length')['sepal_width'].value_counts().unstack().head()

‘sepal_length'が4.5で'sepal_width'が2.3の数=>1
f:id:trafalbad:20180816122214p:plain




機能4
==2つの列A、Bに着目し、3つ目の列Cの値を集計する==

これはわかりにくいので、irisデータで試した。


'sepal_length'でグループを作り(4.3〜7.9)、'sepal_width'の値(2.0~4.4)に
該当する'petal_length'の平均値を集計

df.pivot_table(index='sepal_length', columns='sepal_width',values='petal_length', aggfunc=np.mean).head()

‘sepal_length'が4.5で'sepal_width'が2.3の'petal_length'の平均値は1.3
f:id:trafalbad:20180816122325j:plain



機能5
==特定の型の削除==

「整数('int')・小数('float')・真偽値('bool')、それ以外(’object')」を指定して削除

df.select_dtypes(exclude=np.int)


機能6
==全列の欠損値カウント==

欠損値(Nanのデータ)を数える

print(df.isnull().sum())

機能7
==欠損値の値を置換==

欠損値を別の値に置換する。

例:欠損値を0に置換

df=df.fillna(0)

機能8
==標準化==

データを機械学習のモデルに読み込ませるとき、正規化(標準化)することが多いので、その手法の一つが標準化
numpyとかでもできるが、今回はScikit-learnを使った。

正規化=>データ等々を一定のルール(規則)に基づいて変形し、利用しやすくすること。

標準化=>元データを平均0、標準偏差が1のものに変換する正規化法の一つ

# 標準化
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
dff = sc.fit_transform(df)
dff=pd.DataFrame(dff)

# 列名を元に戻す
listed=list(df.columns)
dff.columns = listed

機能9
==行列内の文字列の末尾のN個削除 (’iraidate'は列名)==

列の文字列の末尾のN個の文字を削除する

b=[]
for i,v in f2.iterrows():
    b.append(str(v['iraidate'])[:3])

# bのDataFrame作成
f=pd.DataFrame(b, columns=['iraidate'])

# 前の同じ行削除
df=df.drop("iraidate", axis=1)

# 横に連結してもとに戻す
df=pd.concat([df, f], axis=1)


機能10
==データの散布図==

Dataframe(df)とplotしたい列名2つを指定

sns.jointplot('kaiin_id','safety_tesuryo', data=df)


pandasデータの散布図の可視化に便利





あとはそのまんまの機能(機能11〜19)

・全列の名前表示

df.columns

・列名の変更

df=df.rename(columns={'last_login_year_cnts1': 'last_login_year_cnt1'})

・列の削除

df=df.drop('列名', axis=1)


・列名の一括変更(df_colum_listに全列名を入れておく)

df.columns=df_colum_list

・地味に全列名の一括変更

df=df.ix[:,['A','B','C']]

・縦にDataFrame(df)を連結

df=pd.concat([df, df2])

・横にDataFrame(df)を連結

df=pd.concat([df, df2], axis=1)

・list(listed)から列名付きで、DataFarame(df)作成

df=pd.DataFrame(listed, columns=['列名'])


・DataFrame(df)をcsvに保存/読み込み

# 保存
df.to_csv('feature.csv')

# 読み込み
df=pd.read_csv('feature.csv')


備忘録もかねてpandasの機能をまとめてみた。使えるものも多いと思うので、気に入ったらストックしておきたい。

異常検知(変化検知)の詳細と特異変換スペクトルと動的時間伸縮法まとめ(機械学習)

異常検知とは、機械学習の一手法で、普通の値のデータの中から極端に大きかったり、小さい値の「異常」なデータを見つけ出すものだ。


異常検知の用途で有名どころは巷では以下のようなものがメインらしい  

 

マーケティング =>流行のブレイクの検出
コンプライアンス  =>情報漏洩/不正検知
・製造系   =>不具合発生検知
・機械系  =>故障検出
・システム運用系  =>障害検出
・ネットワーク運用系  =>トラフィック異常検出
・WEB系  =>トピック潮流変化検出
・セキュリティ系  =>攻撃・侵入検出(コンピュータウィルスDOS攻撃
・サイバー犯罪に使うやつ=>情報潮流、なりすまし


いずれも普通じゃないデータとか、珍しいデータを検出するものだ。


異常検知の3種類
異常検知は主に3つのタイプに分かれる。機械学習シリーズの書籍とかに詳しく載ってるので、ここでは簡単に触れとく。


まず触れておきたいのは異常検知で扱うデータには「定常データ」と「非定常データ」がある。異常検知ではこの2つで使う手法が異なる。
定義は以下の通り


定常データ:同じ波形が続くような時系列データや、統計的にデータ分布図が正規分布(山が一つの単純な曲線)になる


非定常データ:同じ波形が続かない時系列データや、統計的なデータ分布図が正規分布にならない(山が2つ以上の曲線)


・外れ値検知
すでに持ってる(既存の)データから異常なデータや異常箇所を割り出す手法。
正常データと異常データの割合→99:1


くらいのデータ、つまり正常データがほとんどで異常データ少ししかないデータが対象になることが多い。


外れ値検出のほとんどのアルゴリズムは定常データを扱う。
その時は、マハラノビス距離で求められるアルゴリズムに限定させることが多い。代表的なものに、k-means、ランダムフォレスト、one class SVMなどがある

 


・変化検知
これは主に定常/非定常な時系列データなどに多く使うもので、データが大きく変化する点を検知する。googleである「急上昇ワード」が好例だ。扱うデータは非定常データであることが多い。


変化検知で、非定常データを扱うときのアルゴリズムには、いろいろある。メジャーなのは
・特異変換スペクトル
・動的伸縮法
・LSTMなどの時系列を予測するアルゴリズム


などを使うケースが多い。


・異常行動検知
これは多くのユーザーと違う行動をとるユーザーを検出するときに使う。
手法としては、同じ行動をとる多くのユーザーの行動を統計的に確率分布でパターン化し正常データとして扱う。逆に、その確率分布のパターンと異なる分布のデータになるユーザーを異常行動をしたユーザーとみなし、検出する。
ググるとLSTMとかがよく使われてる。

 


特異変換スペクトル(Singular Spectrum Transformation :SST)について

特異変換スペクトルは主に非定常データにおける異常部位の検出に使う(もちろん定常データにも使える)


特異変換スペクトルは、非定常データでもデータが大きく変化してる点(異常箇所)を検出できる。訓練データやテストデータはいらず、検出したいデータだけ用意すれば、numpyで簡単に実装できる
詳しい数式や論理とかは専門書や論文に任せてここではコードで実装みる。


まず検出したいデータは約半年分の検索ワードを2つ(AとB)を取り出して、特異変換スペクトルにかけてみた。

ワードA、Bの可視化

ワードA
f:id:trafalbad:20180809154943p:plain


ワードB
f:id:trafalbad:20180809155015p:plain


 
特異変換スペクトルのメイン実装コードは以下のようになる。ついでに結果を可視化した。

 

from sklearn.preprocessing import MinMaxScaler
 
mss=MinMaxScaler()
train1_frame=pd.DataFrame(mss.fit_transform(df_train1))
train2_frame=pd.DataFrame(mss.fit_transform(df_train2))
 
defembed(lst,dim):
emb=np.empty*1
emb=np.append(emb,tmp,axis=0)
returnemb
 
train1=np.array(train1_frame[0],dtype='float64')
 
w=168# 部分時系列の要素数
m=2# 類似度算出に使用するSVDの次元数
k=int(w/2)# SVD算出に用いる部分時系列数
L=int(k/2)# # 類似度を比較する2つの部分時系列群間のラグ
Tt=train1.size
anomaly_score=np.zeros(Tt)
 
# 異常値のスコアを算出するメソッド
score_list=[]
fortinrange(w+k,Tt-L+1+1):
tstart=int(t-w-k+1)
tend=t-1
# t以前の部分時系列群
X1=embed(train1[tstart:tend],w).T[::-1,:]
# 異常度算出対象の部分時系列群(test matrix)
X2=embed(train1[(tstart+L):(tend+L)],w).T[::-1,:]
 
# X1にSVDを行い上位m次元を抜き出す
U1,s1,V1=np.linalg.svd(X1,full_matrices=True)
U1=U1[:,0:m]
# X2にSVDを行い上位m次元を抜き出す
U2,s2,V2=np.linalg.svd(X2,full_matrices=True)
U2=U2[:,0:m]
 
# U1とU2の最大特異値を取得
U,s,V=np.linalg.svd(U1.T.dot(U2),full_matrices=True)
sig1=s[0]
 
# 最大特異値の2ノルムを1から引くことで異常値を得る
anomaly_score[t]=1-np.square(sig1)
score_list.append(anomaly_score[t])
 
 
score_=np.reshape(score_list,(len(score_list),))
 
# 変化度をmax1にするデータ整形
mx=np.max(score_)
score_=score_/mx
 
# trainデータの異常部位plot
train_for_plot=np.array(train1_frame[0],dtype='float64')
fig=plt.figure()
ax1=fig.add_subplot(111)
ax2=ax1.twinx()
 
p1,=ax1.plot(score_,'-b')
ax1.set_ylabel('degree of change')
ax1.set_ylim(0,1.2)
p2,=ax2.plot(train_for_plot,'-g')
ax2.set_ylabel('original')
ax2.set_ylim(0,12.0)
plt.title("Singular Spectrum Transformation")
ax1.legend([p1,p2],["degree of change","original"])
plt.show()

 

ワードA
f:id:trafalbad:20180809155034p:plain

ワードB
f:id:trafalbad:20180809155048p:plain


 
ご覧の通りAでは1カ所で急上昇し、Bは数カ所でデータが変化してる。いずれも非定常データだが上手く検出できてる。


数値も大きく変化してる点で

・Aが10→80

・Bが800→1800

まで上昇しているから、適切に異常個所を検出できてるっぽい。



特異変換スペクトルの活用方法は個人的に

・異常部位の検出
・訓練データやテストデータのを分ける

のに使えた。

 


閾値の設定について


反対に変化点検出のときに、異常値を判断する「閾値」の設定は特異変換スペクトルでは難しかった。
スペクトル変換しているので、リアルの値とかなり変わっている。そのため、閾値設定には工夫が必要になる。


非定常データの閾値設定には
・動的時間伸縮法(Dynamic time warping:DTW)

・LSTMのような回帰アルゴリズムとMSEの組み合わせ

とかの方が一般的だ。


動的時間伸縮法(DTW)は複数の時系列データ同士の距離(類似度)を計算し、最も高い類似度 or 最も低い類似度の時系列をk個選び、その平均値から閾値を求める。kが大きく設定すると閾値も低くなる。閾値の設定はケースバイケースで設定する必要がある。


DTWのコードはここを参照してほしい。


結論、特異変換スペクトルは定常/非定常に関わらず、どんなデータでも、訓練データとテストデータを分けたり、異常部位を見つけるのに役に立つ。かつ簡単に実装できるので、異常検知では色々な用途に使えるので、オススメの手法だ。

 

大学編入試験の英語(英作文)、口頭試験の対策等、使える情報をまとめてみた

大学編入というと最近では、ネット上でも具体的な情報が出回ってきた。しかし、口頭試験や専門的な科目の情報はまだ少ない。今回は質問をもらったので、編入試験に役立つ情報として口頭試験について、英語の勉強法等、編入あるあるの質問に回答していきたい。

なるべく自分の体験や先輩から聞いた実体験を元に書いてある。なるべく質の高い回答がしたかったので、記事の構成から質問内容は少し変更してあるが、質問内容には全部回答してある。

f:id:trafalbad:20180204162005j:plain




Q1
編入の口頭試験の傾向と対策は?

大学によりけりだが、基本的な問題がほとんどで

・その場でホワイトボードが用意されて問題を解く

・基礎的な知識「ーの定理について説明しろ」を口頭で答えるやつ

とか色々あるようだ。口頭試験でプリントが配れるものは、試験ほど時間は与えられない。その時間内で解いた問題を試験管(教授)にわかりやすく口頭で回答を説明する形式が多いと思う。

例外的なものでは、院試だが、筑波大の知能機能システム専攻で、時間を大量に与えて、そのあと教授に前でプレゼンするっていう口頭試問のパターンがあった。

ともあれ本当に大学によりけりとしか言いようがないので、受ける所をネットでググってみたり、予備校で具体的な情報を仕入れた方が間違いない。


参考:口頭試験、面接の勉強法

ちなみに口頭諮問の勉強法では、

・教科書の例題レベルの問題リストを作る

・頭の中でその正解を自分の言葉で回答できるように考える練習をする

という勉強法があった。体験談だが、この勉強法で論理的な思考力が身につき、本番でもスムーズに回答できるようになるようだ。面接でも同じように質問リストを作ることで同じ練習ができる。参考までに




Q2
専門分野の勉強対策はどうやるの?


大学の教科書で編入試験で必要な基礎知識は身につく。ただし応用問題を解けるようになる必要がある。

基礎ができれば、あとはネット上で高専生や編入に合格した人が使ってる参考書や問題集がメジャーなので、それで問題を解いて思考力をつけれるのがオールマイティな勉強法といえる。

下位の大学は教科書の基礎知識と例題程度で解けるかもしれないが、難関大学は思考力が必要なので、ネット上でメジャーな問題集をやって思考力をつけよう。結果的に過去問が解ければいいのだ。

早い話、教科書で基礎が理解できるなら、あとは問題集をやればいい。基礎ができてて、応用問題に解ければ、過去問は解ける。




Q3
大学では高校のときのように教科書+傍用問題集等のようなものは購入するの?


大学編入も大学1.2年の内容を聞いてくるのは変わりない。大学では基本試験用に問題付きの教科書を購入する。むしろ高校のように編入のために教科書+問題集を購入することはない。

なので自分で編入用の問題集は別途購入する必要がある。高校のとき入試対策ができていても、大学教養課程は難しいし、教授も丁寧に教えてくれないので、どれだけ独学できるかによるだろう。独学ができれば編入も高校と同じ容量で勉強できる。

高校でも簡単な大学は基礎程度の内容を、難関大学は高校の内容でも思考力が必要なひねった問題を出してきたはず。

編入も同じで簡単な大学は基礎問題を、難関大学なら大学1.2年の内容で思考力が必要な問題を出してくる。基本的に高校は暗記重視だが、編入は思考力重視の問題が目立つ傾向がある。





Q4
英語の勉強に予備校は必要?


個人的な考えだが、過去問から対策が立てられれば、編入英語に予備校は必要ない。ただ勉強法または、何をすべきかわからないなら予備校に行った方が教えてくれるので、予備校に行く方がてっとり早いだろう。

過去問から対策を立てて、やることがわかるなら、予備校は過去問だけ手に入れるために利用すればいいと思う。



あと、個人的に編入英語は勉強法を知っておくことが何より有利だ。

英語は人により勉強法は異なるが、根本的な部分は同じで、人によってそのやり方が違うだけだ。

これは参考書ではないが英語の勉強法が詳しく載ってて、もちろん和文英訳の勉強法も載っている。

この本で勉強法を知っておけば、自分で考えて勉強法が構築できるので、まずは参考書に手をつける前にこの本で英語の勉強法について知っておくとかなり有利だと思う。編入の英語対策が立てやすくなる。






Q5
どの予備校がオススメ?


個人的に都心では河合塾KALSと中央ゼミナールを使ってる人が多いと思う。

もし東京に住んでるなら河合塾KALSか中央ゼミナールがオススメだ。なぜかというとネット上の評価もそれなりに高いし、二つは過去問が充実しかつ、実績もある。

しかし、個人的に言いたいのは塾は編入の情報、過去問を得たり、独学で勉強できない科目を補ったりするところであり、あくまで入るための道具に過ぎない。


編入の勉強は自分次第なので、塾に頼り切るよりも自分が勉強を能動的に進めることが非常に重要で、その上で予備校は補助的に使う程度に留めるべきだと思う。勉強は自分で考えて進めている人の方が受かっている傾向が強いのは高校も編入も同じ。

とはいえ人によりけりなので、塾は自分の好みに合わせて使って欲しい




Q6
編入の勉強時期は?


下の編入の勉強法の記事で書いているが、高校で理系出身か、文系出身かで勉強時期が変わる。

trafalbad.hatenadiary.jp


自分は独学でこんな感じでやった。あくまで自分のケースなので、参考程度に。

もし理系出身なら、だいたい1年生の9月くらいから1月くらいまで、基礎を徹底的にやり、2月から3月までが応用として問題集を解く時期。それ以降は過去問をやったり、過去問が解けるようになるまで基礎の反復や思考力を鍛えるのに費やすといいだろう。

まとめるとこんな感じ


1年生の9月〜1月 基礎固め

2月〜3月 応用問題を解く時期

2年4月以降 過去問をやったりする時期



塾の場合はもっとスケジュールもギリギリで詰め込む感じ。2年生で理系の編入なら早いにこしたことはないので、早めに自分なりの勉強法を確立して、余裕を持っておいた方が絶対にいい。




Q7
編入の英語勉強法(基礎編)はどうやるの?


編入の英語は大学によりさまざまだから対策も大学によるので決まった方法はないが、基礎固めは共通している。

英語はまず

・文法

・単語

を確実に固めよう。特に、文法は文法問題のためではなく、「英語をスラスラ読むため」に必要と考えよう。この二つをおろそかにしていると編入の問題は確実に解けるようにならない。

個人的におすすめなのが、

がおすすめ。

効率的な勉強法としては、上にあげた英文法の本を完璧に理解して、速読英単語必修編と上級編レベルがスラスラ読めるレベルまで鍛えることだろう。速単は英作文や和訳にも応用が効くので効率的。

ネクストステージのような類の参考書は、編入向けではないので、コスパの点で無駄が多いのでやらない方がいい。

速読英熟語は基礎的な熟語の把握、またはできたらやるくらいでいいだろう。

また英文解釈の本はたくさん出ているが、英文解釈(読解)に必要なのは基礎的文法の理解と、英文を読む力なので、編入の英語に別に英文解釈の本は必要ない。特に、英文解釈の本は英語に必要な文法を体系立ててまとめてないので、基礎的な英文法習得という点で効率的ではない。

あまり多くの参考書を買わず、これだと決めた参考書を徹底的にやったほうがいい






Q8
編入の英文和訳の勉強法はどうやるの?


和文英訳は英作文と同じ要領で勉強すればいい。まずは定型的な例文を覚えよう。これは「英借文」ともよばれている方法で基礎例文を覚えておけば、英作文を一から考えなくてよくなる。英作文は次の本をどちらかセットでやるだけで十分だと思う。


または




特に「竹岡広信の英作文〈原則編〉が面白いほど書ける本」は英作文をどう書いたらいいのかという考え方が載っているので、オススメだ。



また英作文は回答が決まっているわけではないので、自分で作った回答を添削してくれる人がいるとかなり有利。学校で英語に詳しくかつ、親切な講師に添削してもらったり、予備校を使うのもありだ。自分は留学生と仲良くなって添削してもらっていた。




Q9
編入に必要ない科目の単位について

編入で必要ない科目は単位が取れる程度で十分だ。最低限のことだけやって単位だけゲットし、あとは編入の科目に集中した方がいい。

また、単位の成績だが、編入の面接で聞かれることはほとんどない。理系編入は編入試験の成績がよければ受かる。つまり、基本的に大学の単位の成績はほとんど合否に関係ない。




Q10
大学編入成功のための心得&アドバイス




・あれこれ参考書に手をつけずこれだと決めたものをとことんやりこむ


応用問題よりも基礎固めを重要視しよう。特に高校の基礎知識は絶対に必要。編入は高校の延長なので、高校の基礎知識なしに編入で受かることはない。むしろ受かってる人は高校の基礎固めは十分すぎるほどやっている。



・自分の勉強法を確立させよう

編入試験で落ちる人で多いのは、勉強で何をやっていいのかわからない人だ。予備校でも言われたことを漫然とこなす人は受かってない。

それよりも試行錯誤して自分なりの勉強法を確立した人や、塾でもテキストに能動的に取り組んでる人が受かってるケースが非常に多い。高校で勉強法を知ってるなら、編入試験でもちょっと改良するだけで通用するだろう。

あくまで自分の体験だが、勉強を始める前に勉強法を知ることがかなり効率的だ。自分は勉強法を知るために、東大生とかの勉強法の本を片っ端から読んだ。そしてそれを試し、合わなかったら捨て、あったものがあったら自分なりに改良して使うを繰り返し、試行錯誤して勉強法を確立させてた。



・思考力を鍛えよう

基礎固めをやったら何かと発展問題を暗記してしまう人がいるが、編入試験は思考力がないと解くのが難しい。編入試験でよく使われている問題集で一問一問をじっくり考えよう。そうすることで思考力を鍛えられ、過去問が早く解けるようになる。

塾を利用するならテキストの問題をとことん考えて、思考力を鍛えることをオススメする。



・遊びも必要

編入試験はモチベーションを保つのがとても難しい。周りは遊んでる中、自分だけ勉強するというのはとてもきついからだ。そんなときは志望校に行ってみたりするのもありだが、適度に遊ぶことだ。

勉強だけやっていてモチベーションが維持できず、終盤で力尽きては意味ない。自分は勉強はもちろん遊ぶこともスケジュールに練りこんでた。ここら辺はドラゴン桜にも書いてある。





編入試験はホント大学によって形式は様々なのだが、勉強法の根本的な部分は変わらない。しかし、情報が足りなく、情報戦とはよく言われる。ネット上では手に入らない情報は予備校に行くのがてっとり早いだろう。この記事内容を参考にしたり、その他サイトを参考にして自分なりの対策を見つけて欲しい。



編入系記事
trafalbad.hatenadiary.jp

川田まみの全ベストシングル、アルバムを聴いた僕がオススメする良曲26選【シャナ系あり】

f:id:trafalbad:20160827153041j:plain
川田まみさんの曲はアニメ界では有名で、アップテンポ系からバラードまで心が動かされる曲ばかり。

川田まみは2001年から活動を開始し、音楽制作プロダクション「I’ve」のメンバーとして、アニメやゲーム関連の楽曲を多くリリース。2016年5月21日の「MAMI KAWADA FINAL F∀N FESTIVAL "F"」を最後に引退しました。

とても悲しかったのですが、残された名曲はどれも心に残るものばかりで、良質なインスピレーションをかき立ててくれます。今回は川田まみの曲を全曲聴いた自分が、その中からベストの26曲を選曲して紹介します
*個人的な好みが色濃く出てしまって、どの人にも好みが合うとは限りませんが、どれも自信をもってオススメできる曲構成にしたつもりです。

目次
クールでカッコイイ系
アップテンポ系
バラード系
聴いてて心地いい曲

クールでカッコイイ系

1. U/N

u/n

u/n

灼眼のシャナFinal Ⅲの15話の挿入歌。サビから徐々に盛り上がり、曲の1番のクライマックスの場面でアニメにぴったりのカッコイイシーンを想起させる。灼眼のシャナのオープニングと一緒に見ても鳥肌ものですが、個人的には漫画の「ハンターハンター」のシュールな場面をこの曲を聴きながら読むのが最高です。川田まみさんの中で1番好きな曲の1つ。



linkage

linkage

linkage


『LINKAGE』は、川田まみの3作目のオリジナルアルバムの中の一曲。曲自体は切ない流れになってますが、その中に含まれるサビのシュールさ、かっこよさは秀逸。なぜアニソンにならなかったのか不思議なくらいです。個人的には漫画「ONE PIECE」の新世界、パンクハザード編と合わせて読むのが好き。カッコいいバトル漫画との相性は抜群です。



PSI-missing

PSI-missing

PSI-missing


PSI-missing』とは、川田まみメジャー6thシングルで、アニメ『とある魔術の禁書目録』オープニングテーマ。タイトルを和訳するなら「サイ消失」でこれは超心理学(超能力、超常現象)の用語。曲自体はアニソンに抜擢されるだけあってクオリティも抜群です。始めからかっこいいテンポで始まり中盤のサビでかっこよさは最高潮になります。曲自体はかなり川田まみの中でレベルが高い方だと思います。





parablepsia

parablepsia

parablepsia


川田まみ 5thアルバム「PARABLEPSIA」
の一曲。序盤は曲としてのインパクトは弱めですが、1:17秒からの「回りめぐる世界〜」から突然カッコよくなります。アルバム「PARABLEPSIA」の中では代表曲であり、個人的にかなりの名曲の部類に入る。全体として盛り上がりは少なく、サビでクールに決めるところが特徴といえます。




serment

Serment

Serment


表題曲の「Serment」は、テレビアニメ『灼眼のシャナⅢ-FINAL-』の後期オープニングテーマ。動画で見れば分かる通り、切ない⇨カッコいいという流れで、アニメの映像との相性は抜群。数の多い川田まみの曲の中で、アニメのオープニングに選曲されるのはかなりユーザーからの支持率は高いのが特徴です。「sermant」はアマゾンレビューでも抜群の支持率でした。



going back to square

Going back to square one

Going back to square one


2012年8月8日にジュネオン・ユニバーサル・エンターテイメントジャパンより発売された川田まみの4枚目のオリジナルアルバム『SQUARE THE CIRCLE』の一曲。中盤のサビはシュールに決める流れで、全体的にテンポの良いリズムとカッコいいメロディが融合しています。個人的にこちらも数ある川田まみの曲の中で、アニメに起用されてもおかしくないレベル。



Enchantress

Enchantress

Enchantress


TVアニメ「ヨルムンガンド」OPテーマ。序盤はテンポの低いメロディが流れますが、中盤から一気にカッコよくなります。特に 1:14秒からの「胸を突く雷鳴〜」の部分がクールでカッコよく、アニメのオープニングと見ると鳥肌もの。収録アルバム「PARABLEPSIA」の中ではかなりクールな面でいい曲の部類に入ります。





HOWL

HOWL

HOWL


「CRブラックラグーン2」テーマソング。タイヨーエレックのパチンコ機『CRブラックラグーン2』のテーマソングとしてアルバム『parablepsia』でCD化された。序盤から容赦なく、カッコイイテンポで曲が展開していきます。中盤からテンポが早くなり、テーマソングとしてはかなり相性がいい。収録アルバム『parablepsia』はかなり川田まみの曲では名曲が多く、このHOWLもその中で名曲の1つだと思います。


TRILL

TRILL

TRILL

川田まみ、2年ぶりとなる待望の2ndアルバムSAVIAに収録。序盤からじょじょにテンポが上がっていくが、全体としてとてもクールな曲。パドル漫画のシュールなワンシーンがピッタリです。個人的にはテニプリの全国大会仁王戦vs不二戦のときに聴くのが好き。






masterpiece

masterpiece

masterpiece


ロックテイストと打ち込みを多用したデジタルサウンド、エフェクトの使用、途中にコーラスがあるといった複雑な曲構成は、「科学」と「魔術」が交錯する『とある魔術の禁書目録』の世界観や登場キャラクターの全体的な複雑さをイメージしたものとのこと[2]。「masterpiece」は、テレビアニメで使用される以前の2008年12月末頃より、『とある“ラジオ”の禁書目録』、『井口裕香の超ラジ!Girls』などで先行オンエアされていた。



CLIMAX

CLIMAX

CLIMAX

linkageに収録されている曲。どちらかといえばちょっとテンポ低めだが、シュールな部分は特に際立っている。サビの部分から一気に盛り上がるのが一番印象に残ってます。1:03からのサビの一気に盛り上がるところが印象に残る曲。




アップテンポ系


緋色の空

緋色の空

緋色の空


川田まみの2枚目のシングル収録曲。テレビアニメ『灼眼のシャナ』の前期オープニングテーマとして制作された楽曲。言うまでも無いですが、アニメに選曲されるということは名曲でユーザーの支持率も高いです。

アニメのオープニングのスピード感と、曲のスピード感のマッチングは絶妙。全体的なアップテンポ調が強く出ていて、盛り上がらずにはいられない一曲。





Break a spell

Break a spell

Break a spell


TVアニメ「東京レイヴンズ」新EDテーマ。序盤からだんだん盛り上がり、中盤のサビで、もっともヒートアップする構成になっています。「東京レイヴンズ」は若年齢層向けのライトノベルで「Break a spell」のテンポとは相性抜群。実はカラオケで歌いにくい。個人的に漫画「食戟のソーマ」の秋の選抜のシーンとのマッチングが気に入ってます。




JOINT

JOINT

JOINT


川田まみの5作目のシングル収録曲。 2007年10月31日のテレビアニメ『灼眼のシャナII』前期オープニングテーマ。『灼眼のシャナ』系の曲はアップテンポで、それに起用される川田まみの曲もまたアップテンポです。アップテンポでもかっこよさがにじみ出て、聴いてるだけで盛り上がること間違いなし。テンポの早いバトル漫画を読みながら聴くと、臨場感がアップしますよ。



Eager eyes

Eager Eyes

Eager Eyes


アルバム「PARABLEPSIA」収録曲。ユーモラスなメロディからアップテンポ展開する構成。1:03秒から特に盛り上がる展開に。アニメ『灼眼のシャナ』系の曲と比べて、若干テンポは劣るものの、充分アンプテンポといえる名曲です。




I civilization

I...civilization

I...civilization

アルバム「PARABLEPSIA」(作曲:高瀬一矢、編曲:HARD STUFF) に収録されている曲。序盤はとてもテンポが低くから始めるが、じょじょにテンションが上がる。


「PARABLEPSIA」は全アルバムの中でとてもいいタイプだと思います。曲はどちらかといえばクールな方だけど、とても澄んでいるのにテンションが上がる曲。



バラード系


悲しみの森

悲しみの森

悲しみの森


2006年3月、川田まみの1作目のオリジナルアルバム『SEED』の収録曲。バラードとはいえ、要所にカッコイイ要素の入る川田まみの曲の中では最も純粋なバラード。神秘的ともいえるそのメロディは、感傷に浸りたいときにはグッド。1:46秒の「深くなるこの傷口をさらに深く〜」の部分が、曲の中で最も純粋なバラードだと思います。





see visions

See visionS

See visionS


とある魔術の禁書目録Ⅱ」のオープニングテーマ。序盤では怖いイメージの曲が流れますが、サビからアニメのオープニング映像にぴったりのバラード調になります。特に第2サビからは、アニメのオープニングでは聴けないような勇気づけられるような曲調になっています。


言葉、心の声

言葉、心の声

言葉、心の声


linkageのCDに収録されている曲。バラードの中でも多分ベスト3に入る曲調。雨の日に聴いたり、物思いにふけるときに聴く曲に抜群。サビからの「今その胸の〜」部分が一番その部分が強調される。

序盤からバラードとわかる曲で全体にわたりそれが一貫してる。聴き終わった後にとても気持ちいい。



PIST

PIST

PIST


アルバム「PARABLEPSIA」収録曲。序盤に重低音の曲から、中盤のテンポの早いバラードに展開していくのが特長。川田まみの中では、かなり平凡なバラードと言えるかもしれません。若干カッコいい要素が入っているのですが、全体的にバラード調なのでバラード系に分類しました。


in answer

in answer

in answer

川田まみの3作目のオリジナルアルバム『LINKAGE』に収録されている。バラードというか曲のタイプを分けづらいが、シュール&バラードという感じ。サビのあたりが一番盛り上がるが、サビ前の方が個人的に好き。



Replica_nt

Replica_nt

Replica_nt


川田まみの5作目のシングル収録曲。序盤は平凡な流れですが、バラードとしての味が出てくるのは1:30秒あたりからです。川田まみのバラード系としては落ち着いていて、他のバラード曲とは若干、趣が違う曲となっています。個人的には「一回で曲の良さがわかる」というより、「何回か聴いてるうちにわかる」タイプの曲でした





another planet

another planet

another planet


川田まみの2枚目のシングル『緋色の空』収録曲。0:45秒「ほほえむ満月〜」からバラード調が絡みはじめ、2:03秒の「遥か遠くへ〜」からバラードとしては最も静かで最高潮になります。川田まみの曲としてはカッコイイ要素がなく、全体的な切ないメロディ構成の典型的なバラードですね。




聴いてて心地いい曲

TOY

TOY

TOY

『LINKAGE』(リンケイジ)に収録されてる曲。シュールでもアップテンポでもないが聴いてて悪くない。序盤からは曲調がわかりにくいだが、サビから本調子になってくる。

聴いた後に気持ちよくなる曲なので良曲に分類しました。




radiance

radiance

radiance

アニメ『スターシップオペレーターズ』のオープニング・テーマ、エンディング・テーマを収録したシングルの一曲。2005年2月23日に発表された1stシングル「radiance」。とてもクールな曲だが、カッコいい。曲の中では端っこの存在っぽいと思いきや聴いてみて全然悪くない一曲。




live a lie

live a lie

live a lie

SQUARE THE CIRCLEに収録されている一曲。とてもカッコいい曲でスピード感、テンポともに悪くない。曲全体の統一感が素晴らしく、シャナ系と違うタイプの名曲。

サビの前やサビに入ってからもとても澄んでる。個人的にナルトのラストバトルシーンに聴くのが一番好き。






川田まみの曲は今後リリースされることはないので、備忘録がてらまとめてみました。他のアーティストも良曲が多いので、機会があればまとめてみたいと思います。

DCGANで生成した訓練画像を使ってCNNで画像分類してみた【Deep learning】

以前からDCGANで生成した画像でデータセットを作ろうと思い、画像生成をしていたが、ようやくそれっぽい画像ができた。


f:id:trafalbad:20171028214149p:plain

今回はDCGANで作成した画像を「訓練画像」に、DCGANに食わせた元画像を「テスト画像」にして、CNNで分類してみることにした。CNNは以前、VGGNetモデルのCNNで画像分類したやつを使用。
「DCGANで作成した画像は、データセットに使えるのか」という試みで、その結果を書いていきたい。

trafalbad.hatenadiary.jp


目次
・全体の行程
・DCGANで訓練画像生成
・CNNでテスト画像分類


全体の行程
まず全体の作業行程のポイントは次の通り



・523枚のマンションの外観画像(外から見たマンションの建物の画像)を増幅した34860枚を、DCGANに読み込ませ画像を生成


・生成画像の中から、よさげな画像7040枚を訓練画像に採用


・DCGANに読み込ませた元画像523枚をテスト画像にする


・DCGANで生成した訓練画像をCNNで学習、テスト画像を分類できるか試す




これが全体の行程だ。CNNは以前、VGGNetを参考にマンション画像分類をしたものを使用し、それに今回新しく作った「マンションの外観」の訓練画像とテスト画像をプラスして、新たに正解率を評価する。

上手くいけばDCGANはCNNのデータセットも作れることの証明になる。

けど、結論から書くと上手くいかなかった。今回はそれをまとめていこうと思う。




DCGANで訓練画像生成


画像の用意

今回、生成するのは「マンションの外観」画像。元画像はGoogle画像から収集し、それを増幅。

増幅させるやり方は前の記事で書いてるので、それをそのまま使用し、523枚から31360枚に増幅。特に”角度変換”を何回も使った。

f:id:trafalbad:20171028215445p:plain



あとはDCGANにかけるだけ。最初、jupyter用のDCGANコードを書いたんだけど、broken pipeが出てカーネルが死ぬとまた最初からやり直しになるので、全くメリットがなかった。

なので普通にローカルで作成するものを作った。




作成画像

48エポック(だいたい34800ステップ)ほど回してできた画像がこちら。だいたい100ステップごとに冒頭に貼ったような1枚64pxの8×8=64枚、512pxの塊がフォルダに保存されていく。

f:id:trafalbad:20171028215711g:plain



ほぼ外観とそっくりなのでいいできだと思う。ただアップで見るとクレヨンで描いたような画質なので、元画像と比べてドット感がないのが少し気になる。


f:id:trafalbad:20171028214218j:plain



生成画像の中から、よさげな画像を別のファルダに入れていった。だいたい60枚くらいたまったら、この512pxの塊を単一の64pxの画像にバラす。



DCGANの訓練が終わってから画像を生成してもいいんだけど、訓練後に生成すると全て同じ画像になってしまうので、訓練途中にできた中から、出来のいいのを採用。


64枚、512pxの塊は下のコードで一気に分解できる。
64枚の塊画像をまとめて保存したファルダパスと、分解した画像を保存するファルダパスを指定して実行すれば、勝手に保存されていく。

import os
import sys
import scipy.misc
import cv2, matplotlib
import numpy as np


path_restore="DCGAN生成画像のフォルダパス"

path_save='分解画像保存先のフォルダパス'
images = os.listdir(path_restore) # 画像の読み込み


rows = []  # 横に分解
for i in images[1:]:
       rows.append(np.hsplit(cv2.imread(path1+'/'+i),8))


tin=[] # 縦に分解
for idx in range(len(rows)):
    for sec in rows[idx]:
        tin.append(np.vsplit(sec,8))


count=0
for idx in range(len(tin)):
    count+=1
    for sec in tin[idx]:
        count+=1
        scipy.misc.imsave(path_save+'/split{:04d}_{:02d}.jpg'.format(idx, count), sec)

こうして、CNNに読み込ませる訓練画像、約3520枚ができたので、”左右反転”をかけ、増幅して7040枚に。あとはDCGANに読み込ませた画像(増幅前)の元画像523枚をテスト画像にして、CNNで上手く判別できるかを試す。




CNNでテスト画像分類

訓練画像とテスト画像

さっきも書いたけど、


テスト画像 → DCGANに読み込ませた訓練画像(増幅前)の元画像523枚

訓練画像 → DCGANからできた画像の中から、出来のいいやつを分解した7040枚

にした。


CNNは前にマンション関連の画像を判別するために、あらかじめ作ってあるやつを使う。そのときのラベル0〜3の訓練画像とテスト画像に、今回の画像をプラスする。


今回プラスする分は新しくラベル4にした。CNNのコードは数カ所の数値を書き換えただけ。
前のサイトで書いてるけど、訓練画像とテスト画像の内訳は改めて書くと、こんな感じ。



訓練画像→ 合計 32340枚

Label 0 玄関 50, 廊下 50, リビング 150, 寝室 100, クローゼット 100 (増幅して6300枚)

Label 1 キッチン 300 (増幅して6000枚)

Label 2 トイレ 400, 風呂 150, 洗面化粧室 150 (増幅して7000枚)

Label 3 眺望 300枚 (増幅して6000枚)

Label 4 外観 3520枚 (増幅して7040枚)



テスト画像→ 合計 1063枚

Label 0 玄関 30, 廊下 30, リビング 30, 寝室 30, クローゼット 30 (150枚)

Label 1 キッチン 115(115枚)

Label 2 トイレ 80, 風呂 40, 洗面化粧室 40 (160枚)

Label 3 眺望 115 (115枚)

Label 4 外観 523枚(523枚)


新しくプラスした訓練画像も十分な枚数。DCGANから生成された画像でも、別に読み込ませた元画像に似てるわけではないので、元画像をテスト画像にしてみた。


仮にこれが上手くいけば500枚くらい画像を集めれば、訓練画像とテスト画像ができることになるので、ある意味画期的な手法ってことになる。



正解率

結果、正解率は48%

メッチャ低い。以前のCNNでは87.8%だったので、理由が知りたい、というわけでラベルごとの正解率をまとめてみた。

Label 0 80%

Label 1 83.9%

Label 2 91.5%

Label 3 94.5%

Label 4 3%



結果は見ての通り、ラベル0〜3は普通に高いのに、DCGANで作成した画像のラベル4が「3%」だった。



原因を考察してみる

外観画像は、ラベル3の眺望と結構似てるから

ラベル3の訓練画像に数枚ほど、外観とかなり似ている画像が含まれていたため、ラベル3に分類されたかもしれない。けど、数枚なのでその可能性は低い。
それよりもラベル3の眺望自体、他の建物も写ってるので、ラベル4の外観と似てる。そのため、ラベル4がラベル3に分類されてしまった可能性もある。

f:id:trafalbad:20171028214249p:plain


ちなみに純粋にラベル4の外観画像を、訓練画像、テスト画像ともにGoogle検索で収集したもので判別したときは、正解率は高かった。


DCGANで作成した画像は、元画像とは特徴抽出が違くて、まったく別物だから

生成された外観画像を画面上で見ると、人間には「外観」に充分見えるんだけど、CNNには全く別物に見えるらしい。多分、特徴量が本物の外観と全然違うのが一番の原因と考えられる。

スギャーンメモのサイトでも人間の顔と全く違う特徴抽出をしていたらしい。もう少し精巧に作れば、本物と同じように特徴抽出されるのかわからない。

とはいえ3%は低すぎるので、DCGANは本物の元画像とは似ても似つかないもののようだ




結論

やはり乱数から生成した画像は、人間には本物ぽくても、機械には別物で、本物同様の特徴抽出はされないことが原因っぽい。

外観の本物の画像を訓練画像に使った時は、ふつうに正解率が高かったことを考えると、やっぱりDCGANで作った画像は本物と違うらしく、データセットにはできないようだ。




今回はDCGANで生成した画像を使って、読み込ませた元画像を分類することができるか試したが、見事にコケた。CNN自体はかなり精度の高いものを使っているので、やっぱりDCGANの作成画像は本物も全く違うところに問題がある。
アイドル画像分類とかも、DCGAN生成画像を訓練画像にするなんてことは絶対できないだろうから、画像分類のデータセットはやっぱりキチンと集める必要がありそうだ。

VGGNetを参考にしたCNN(tensorflow)でマンション関連の画像を分類してみる

マンション関連の画像をCNNで分類する試みをしてみた。普通のcifar-10のモデルだと正解率が低かったが、VGGNet(Visual Geometry Group Networks)と呼ばれる高性能のCNNを実装しているサイト(すぎゃーんメモ)があったので、試しに実装してみたところ正解率87.8%を達成することができた。今回はそのログとして詳細を書いていこうと思う。


目次
1.画像の内訳
2.CNN
3.ラベル毎の正解率



1.画像の内訳

まず、訓練とテストに使った画像の枚数と、その内容の内訳は次の通りだ。


訓練画像→ 合計 25300枚
Label 0
玄関 50, 廊下 50, リビング 150, 寝室 100, クローゼット 100 (増幅して6300枚)

Label 1
キッチン 300 (増幅して6000枚)

Label 2
トイレ 400, 風呂 150, 洗面化粧室 150 (増幅して7000枚)

Label 3
眺望 300枚 (増幅して6000枚)



テスト画像→合計 540枚
Label 0
玄関 30, 廊下 30, リビング 30, 寝室 30, クローゼット 30 (150枚)

Label 1
キッチン 115(115枚)

Label 2
トイレ 80, 風呂 40, 洗面化粧室 40 (160枚)

Label 3
眺望 115 (115枚)

ラベル0〜3までの4種類で訓練画像は25300枚、テスト画像は540枚。
下にラベル毎の画像の一部を抜粋した。96pxで、左からラベル順になっている。

f:id:trafalbad:20170930083205p:plain

元画像は少ないものの、画像をうまく増幅させた(increacing_images.py)。画質に変化を加えるのは原則一回として、画質にあまり影響のない範囲で増幅させた。


元画像→ガンマ変換→コントラスト→.......→左右変換→角度変換

#左右変換
flip_img=[]
for i in seen5000:
    flip_img.append(cv2.flip(i, 1))

#角度変換
rad=np.pi/90 # circumference ratio
# distance to move to x-axis
move_x = 0
# distance to move to x-axis
move_y = 96 * -0.000000005
 
matrix = [[np.cos(rad),  -1 * np.sin(rad), move_x], [np.sin(rad),   np.cos(rad), move_y]]
 
affine_matrix3 = np.float32(matrix)
afn_90=[]
for i in gaikan:
    afn_90.append(cv2.warpAffine(i, affine_matrix3, size, flags=cv2.INTER_LINEAR))

”ガンマ変換”や”コントラスト”は元画像に一回しか適用できない。例えば”ガンマ変換”した画像にさらに”コントラスト”を適用すると画質がやばくなる。

しかし、”左右変換”と”角度変換”は”ガンマ変換やコントラスト”に適用しても画質は変換しないので、単純に全画像数を2倍にできる。最後にこの2つを持ってくることで効率よく画像を水増しできる。

”角度変換”は値を変えれば何回でも使えるので増幅にはおすすめだ。





2.CNN

元々はcifar-10のモデルで正解率を出したが、70%くらいだったので、もっといいのはないかと探していたところ、アイドル画像分類というサイト(すぎゃーんメモ)でVGGNetを参考にしたモデルが紹介されていた。かなりの精度だったので、今回はこれを適用してみた。


結果は87.8%というかなり精度の高い結果。cifar-10のシンプルなものよりも確実に上がっている。もっと層を厚くすれば正解率は上がりそうな気もするけど、精度としては十分なので、これを使った。


ただ唯一違うのはバッチノーマライゼーションを適用してる点だ。別に適用しなくても正解率はほとんど変わらないが、プーリング層だけのモデルと比較して2%ほど正解率が上昇した。

def cnn(x):
    BATCH_SIZE = 128
    def _variable_with_weight_decay(name, shape, stddev, wd):
        var = tf.get_variable(name, shape=shape, initializer=tf.truncated_normal_initializer(stddev=stddev))
        if wd is not None:
            weight_decay = tf.multiply(tf.nn.l2_loss(var), wd, name='weight_loss')
            tf.add_to_collection('losses', weight_decay)
        return var

    def _activation_summary(x):
        tensor_name = x.op.name
        tf.summary.histogram(tensor_name+'/activations', x)
        tf.summary.scalar(tensor_name + '/sparsity', tf.nn.zero_fraction(x))

    with tf.variable_scope('conv1') as scope:
        kernel = _variable_with_weight_decay('weights', shape=[3, 3, 3, 32], stddev=0.1, wd=0.0)
        conv = tf.nn.conv2d(x, kernel, [1, 1, 1, 1], padding='SAME')
        biases = tf.get_variable('biases', shape=[32], initializer=tf.constant_initializer(0.0))
        bias = tf.nn.bias_add(conv, biases)
        conv1 = tf.nn.relu(bias, name='conv1')
        _activation_summary(conv1)
    pool1 = tf.nn.max_pool(conv1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='SAME', name='pool1')
    norm1 = tf.nn.lrn(pool1, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name='norm1')
    
    with tf.variable_scope('conv2') as scope:
        kernel = _variable_with_weight_decay('weights',shape=[3, 3, 32, 64],stddev=0.1,wd=0.0)
        conv = tf.nn.conv2d(norm1, kernel, [1, 1, 1, 1], padding='SAME')
        biases = tf.get_variable('biases', shape=[64], initializer=tf.constant_initializer(0.0))
        bias = tf.nn.bias_add(conv, biases)
        conv2 = tf.nn.relu(bias, name='conv2')
        _activation_summary(conv2)
    pool2 = tf.nn.max_pool(conv2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='SAME', name='pool2')
    norm2 = tf.nn.lrn(pool2, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name='norm1')
    
    with tf.variable_scope('conv3') as scope:
        kernel = _variable_with_weight_decay('weights',shape=[3, 3, 64, 128],stddev=0.1,wd=0.0)
        conv = tf.nn.conv2d(norm2, kernel, [1, 1, 1, 1], padding='SAME')
        biases = tf.get_variable('biases', shape=[128], initializer=tf.constant_initializer(0.0))
        bias = tf.nn.bias_add(conv, biases)
        conv3 = tf.nn.relu(bias, name='conv3')
        _activation_summary(conv3)
    pool3 = tf.nn.max_pool(conv3, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='SAME', name='pool3')
    norm3 = tf.nn.lrn(pool3, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name='norm1')
    
    with tf.variable_scope('conv4') as scope:
        kernel = _variable_with_weight_decay('weights',shape=[3, 3, 128, 256],stddev=5e-2,wd=0.0)
        conv = tf.nn.conv2d(norm3, kernel, [1, 1, 1, 1], padding='SAME')
        biases = tf.get_variable('biases', shape=[256], initializer=tf.constant_initializer(0.0))
        bias = tf.nn.bias_add(conv, biases)
        conv4 = tf.nn.relu(bias, name='conv4')
        _activation_summary(conv4)
    pool4 = tf.nn.max_pool(conv4, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='SAME', name='pool4')
    norm4 = tf.nn.lrn(pool4, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75, name='norm1')
    
    with tf.variable_scope('fc5') as scope:
        dim = 1
        for d in pool4.get_shape()[1:].as_list():
            dim *= d
        reshape = tf.reshape(pool4, [BATCH_SIZE, dim])
        weights = _variable_with_weight_decay('weights', shape=[dim, 1024],stddev=0.02, wd=0.005)
        biases = tf.get_variable('biases', shape=[1024], initializer=tf.constant_initializer(0.0))
        fc5 = tf.nn.relu(tf.nn.bias_add(tf.matmul(reshape, weights), biases), name='fc5')
        _activation_summary(fc5)

    with tf.variable_scope('fc6') as scope:
        weights = _variable_with_weight_decay('weights', shape=[1024, 256],stddev=0.02, wd=0.005)
        biases = tf.get_variable('biases', shape=[256], initializer=tf.constant_initializer(0.0))
        fc6 = tf.nn.relu(tf.nn.bias_add(tf.matmul(fc5, weights), biases), name='fc6')
        _activation_summary(fc6)

    with tf.variable_scope('fc7') as scope:
        weights = _variable_with_weight_decay('weights', [256, NUMCLASS], stddev=0.02, wd=0.0)
        biases = tf.get_variable('biases', shape=[NUMCLASS], initializer=tf.constant_initializer(0.0))
        fc7 = tf.nn.bias_add(tf.matmul(fc6, weights), biases, name='fc7')
        _activation_summary(fc7)

    return fc7   # shape=(BATCH_SIZE, NUMCLASS)

すぎゃーんメモではアイドル画像という単体の物体に適用していたが、今回はマンション関連の画像を対象にしたところがキー。どこが違うのかというと、マンション関連の画像は結構いろいろな関係ない物体が写っているので、

トイレ=トイレ
キッチン=キッチン

とキレイに分類できないところで、CNNも上手く分類できないのではないか心配だった。

しかし、ラベル設定を適切にして、ラベルを貼る人間がこれは「キッチンだな」とか「室内やな」みたいに確信を持って言えるほど混乱しない画像なら、CNNでも高い正解率は叩き出せるっぽい。

各ラベル毎の正解率は次にまとめた。





3.ラベル毎の正解率

各ラベル毎の正解率は以下の通り


Label 0 96.1%

Label 1 77.3%

Label 2 92.5%

Label 3 93.9%

トータルの正解率は87.8%だけど、ラベル毎の正解率を見てみるとやっぱり、キッチン画像がかなり正解率が低い。





→理由を考えてみる


室内 (Label 0)
→とりあえずベットとか天井とかあるので、室内とはわかる

キッチン (Label 1)
→基準が「コンロと水面台が写ってること」なので、それ以外にもいろいろ写ってるとわかりにくい

トイレ (Label 2)
→便器があればとりあえずわかる

眺望 (Label 3)
→景色の一部が画像の大半を占めているので、多分一番わかりやすい



キッチンは「コンロと水面台が写っている」を基準にした。しかし、他に別なものが写ってる確率が高いキッチンは、ラベル0の”室内”にカウントされてしまうっぽい。

下の画像はキッチンの画像だけど、人間でも「室内」といわれれば、そう見えなくもない感じなので、機械にも分類しにくいんだなと考えられる。


f:id:trafalbad:20170930141735p:plain



これが多分、正解率が下がった原因。ここは明確にキッチンとわかる画像を選べばいいんだけど、汎用性が高くないと意味がない。結局、慈悲は無粋だなというわけで、ちょっとわかりにくいのを配置してみたら結果的に正解率が微妙になった。


今回はマンション関連の画像を分類してみた。なんかいろいろとCNNの分類例はある。

けど、動物や顔だったりの単体の物体ばっかりで、鍋料理みたいにいろいろ入ってるマンション画像を分類する例はあまりないのではと思った。けどまあ、適切にラベル設定をして、ラベル付する人間でも迷わない範囲の画像ならCNNでも高い精度は出た。




*one-hot表現について
参考にしたすぎゃーんメモでは損失関数のところでone-hotを適用してたけど、cifar-10形式では特に必要ないらしい。ただしラベルは0から始めないといけないらしく、出力層(NUMCLASS)が4でもラベルは0〜3にしないといけない。これで一回ハマったので、注意。



本当はDCGANでデータセットを作ってから分類させようとしたんだけど、諸々の事情でCNNを先に試しました。DCGANは上手くいったらまた別記事で書く予定。