ありまっくすのへっぽこ日記

アラフォーでへっぽこエンジニアをしております。へっぽこなりに頑張っています。

連休中にやったこと

連休中、何も目標がないまま過ごすとダラダラしてしまうので、GW 10Days Boot Camp!なるものに参加しました。

 

10日間でやったこと

  • 株価スクリーニングシステムの前処理
  • UX DAYS TOKYOの復習会資料作成
  • ダイエットのためのジョギング
  • 秘密基地での宴会

株価スクリーニングシステムの開発

ずっとやりたいと思っていた、株価スクレイピングシステムの開発に着手しました。

結果としては、前処理だけで10日間が過ぎてしまったのですが、試行錯誤することができました。

 

株価データおよび、財務情報の取得

株価データ(日足)を以下のサイトから取得し、財務情報をYahooファイナンスから取得しました。

stock-databox.net

銘柄コードのリストを作成し、Yahooファイナンスからスクレイピングし、csvファイルに出力するプログラムを作成しました。

 

株価データへ、分割および併合のデータを反映

株の分割や併合によって株価が変動するため、分割および併合のデータを反映しました。コーポレートアクションは以下のサイトからスクレイピングしました。

mst.monex.co.jp

 

日足データを年のデータにまとめる

一通り、データが揃ったら日足データを年のデータ(年の4本値)にまとめました。

 

これからやりたいこと

前年度の4本値および財務状況をパラメータに、1年を通して値上がりしている銘柄を学習させたいなーと思ってます。

 

前処理を試行錯誤しながらやっていたので、なかなか進まなかったですが、勉強にはなりました。

ちなみに

ランニングも連休中に行なっていました。合計で110キロほど。

1日あたり20キロほど走ったのが4日間。5日間は軽いジョグ。1日だけ休みました。

フォトリーディング講座を受講してきたよ

またまた久しぶりのエントリー。

実は10月末でプロジェクトがポシャってしまい、12月以降の仕事を決めた上で11月を無職期間にしました。

その間はチェコに行ったり、家の断捨離したり、Pythonや数学の勉強したりと普段やれないことをやっていました。

そして、11/28〜11/29にフォトリーディングの講座を受けてきました。ずっと気になっていたのですが、2日間丸々使える機会がなくて。。無職期間がチャンスと思い、受講しました。

受講したのはこちらの講座です。

www.almacreations.jp

 

フォトリーディング講座で発見したこと

フォトリーディング講座で発見したことは大きく二つあります。

フォトリーディングで全て把握できるわけではない

フォトリーディングはあくまでも「内容の大枠を把握する」ものであって、全てを完璧に覚える魔法ではありません。

フォトリーディングを行った後に、通常の読書を行うと読むスピードは上がります。

フォトリーディングはページをパラパラめくるだけではない

フォトリーディングというと、こんな風にページをパラパラめくるというイメージがあると思います。

www.youtube.com

しかし、それだけでは不十分で、他にもステップがあります。中でも重要なのが「復習」です。(詳細は本とか読んでね)

 

実際にフォトリーディングしてみた

講座では各自が読みたい本を持っていき、実践としてフォトリーディングを行いました。私はこの本をフォトリーディングし、内容を発表しました。

www.amazon.co.jp

参加された方に、すでに読んだ方に「全体はつかめていますよ」と言っていただきホッとしたのでした。

 

Springで複数DBに接続する方法

かなーり久しぶりのエントリー。

仕事でSpringを使っているのですが、複数のDBに接続する際にちょっとはまってしまったのでメモ。

主にやることは以下の通り。

  • application.ymlに接続先DB情報を記載
  • それぞれのDBに対してConfigurationを作成
  • AbstractRoutingDataSourceのサブクラスを作成
  • HandlerInterceptorAdapterのサブクラスで、URLによってスキーマタイプを設定
  • Configurationを読み込むクラスを作成

application.ymlに接続先DB情報を記載

application.ymlへ以下のように接続先DB情報を記載する

spring:
  datasource:
    primary:
      url: jdbc:mysql://localhost:3306/primary
      username: root
      password:
      driverClassName : com.mysql.jdbc.Driver
    secondary:
      url: jdbc:mysql://localhost:3306/secondary
      username: root
      password:
      driverClassName : com.mysql.jdbc.Driver

それぞれのDBに対してConfigurationを作成


@Data
@Component
@ConfigurationProperties(prefix="spring.datasource.primary")
public class PrimaryConfiguration {

	private String driverClassName;
	private String url;
	private String username;
	private String password;

	@Bean
	public DataSource createDataSource() {
		return DataSourceBuilder
				.create()
				.driverClassName(driverClassName)
				.url(url)
				.username(username)
				.password(password)
				.build();
	}
}

secondaryも同じように作成します。

AbstractRoutingDataSourceのサブクラスを作成

まずはThreadLocalでスキーマタイプを保持するクラスを作成する


public class SchemaContextHolder {

	private static ThreadLocal contextHolder = new ThreadLocal();

	public static void setSchemaType(String type) {
		contextHolder.set(type);
	}
	public static String getSchemaType() {
		 if (contextHolder.get() != null) {
			 return contextHolder.get();
		 }
		 return "Primary";
	}
	public static void clear() {
		contextHolder.remove();
	}
}

その上で、AbstractRoutingDataSourceのサブクラスを作成


public class RoutingDataSourceResolver extends AbstractRoutingDataSource {
	@Override
	protected Object determineCurrentLookupKey() {
		// TODO 自動生成されたメソッド・スタブ
		if (SchemaContextHolder.getSchemaType() == null) {
			return "Primary";
		}
		if (SchemaContextHolder.getSchemaType().matches("Secondary")) {
			return "Secondary";
		}
		return "Primary";
	}
}

HandlerInterceptorAdapterのサブクラスで、URLによってスキーマタイプを設定

HandlerInterceptorAdapterのサブクラスで、スキーマタイプを切り替える処理を入れる。


public class Interceptor extends HandlerInterceptorAdapter{
	@Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

		String url = request.getRequestURI();

		if (url.contains("/secondary/")) {
			 SchemaContextHolder.setSchemaType("Secondary");
		} else {
			SchemaContextHolder.setSchemaType("Primary");
		}
        return true;
    }
}

Configurationを読み込むクラスを作成

Configurationを読み込むクラスを作成する。


@Component
@Configuration
public class DataSourceConfig {

	@Autowired
	private PrimaryConfiguration primaryConfiguration;

	@Autowired
	private SecondConfiguration secondConfiguration;

	@Bean
	@Primary
	public RoutingDataSourceResolver getRoutingdataSource() {
		RoutingDataSourceResolver resolver = new RoutingDataSourceResolver();

		Map<Object, Object> dataSources = new HashMap<Object, Object>();
		dataSources.put("Primary", primaryConfiguration.createDataSource());
		dataSources.put("Secondary", secondConfiguration.createDataSource());

		resolver.setTargetDataSources(dataSources);

		// default datasource
		resolver.setDefaultTargetDataSource(primaryConfiguration.createDataSource());

		return resolver;
	}

	@Bean
	public HandlerInterceptor datasourceInterceptor() {
		// Interceptorの設定
		return new com.kajishima.common.Interceptor();
	}

	@Bean
	public MappedInterceptor interceptorMapping() {
		return new MappedInterceptor(new String[] { "/**" }, datasourceInterceptor());
	}
}

コードはこちら

github.com


DeepLearning講座(第9回)を受けてきたよ

8/3にDeepLearning講座の9回目を受けてきました。

ついにラスト2回。結構長かったな。。

今回は、強化学習とYOLO(実装)を学習しました。で、その前に

前回書ききれなかったこと

まずは前回力尽きた部分の続きから。生成モデルVAEとGANについて。二つとも画像の生成ができますが、現在の主流はGANです。

VAE(Variational Auto Encoder)

学習データを与えると、その学習データに似ているデータを生成できる生成モデルです。その名の通り、Auto Encoderを使っています。

AutoEncoderは、入力層にデータを入れるとデータをEncodeして隠れ層に渡し、データをDecodeして出力層から出力します。VAEではXとYが同じものを出力するようにモデルを学習させます。

 

 

f:id:ari_max:20180814222651p:plain

Auto Encoder

 

サンプリングしたXを使ってX=Yになるように学習するため、「ノイズが取り除かれ」かつ、「似てるものは近くに、そうでないものは遠くになる」ようにガウス分布正規分布)からサンプリングします。

 実際にはガウス分布の平均μと分散σ^2を出力する部分とそこからサンプリングする部分に別れています。

f:id:ari_max:20180814223003p:plain

ガウス分布からサンプリングする

 

ガウス分布を使うと数学的に扱いやすくなるので、学習もしやすいのですが、生成データがぼやけてしまうという欠点があります。そのため、VAEが使われなくなったのです。

 

GAN

GANはGenerative Adversarial Networksの略で、2014年にGoodfellowが提案したもの。VAEのようにデータに対する仮定が少なくて済むのですが、とにかく学習が大変なのです。

ですが、こんなこともできます。(実務では使わないそうです)

 

GANの仕組みは下図のように、GeneratorとDiscriminatorという2つのニューラルネットワークを使います。

f:id:ari_max:20180814232750p:plain

GAN

以下のようなフローで、Generatorが本物と見分けがつかないようなデータを生成するようになります。

  1. Generatorが生成したデータと本物のデータをランダムにDiscriminatorに入力する
  2. Discriminatorは入力されたデータが、「Generatorが生成したものか」それとも「本物のデータ」かを判別する
  3. GeneratorはDiscriminatorに「ニセモノ」だということがバレないように性能をあげる
  4. Discriminatorは「ニセモノ」であることを正確に判断できるように性能をあげる

最初に生成するデータがメチャクチャなので、どうしてもDiscriminatorが「ニセモノ」と判別しやすくなってしまいます。したがって、最初はDiscriminatorの精度を落としておくことが重要で、この調整が非常に面倒なのです。

最終的にはDiscriminatorは不要になります。

 

GeneratorはDiscriminatorに見抜かれる確率を最小化し、DiscriminatorはGeneratorが生成した画像を見抜く確率を最大化します。それを満たすための式が以下の通りです。

  • D(x): 入力が学習データからきている確率
  • x~pdata(x): 学習データの分布に従うデータ
  • x~pz(z): Gから生成された出力の分布に従うデータ
  • E [ X ]: 期待値

f:id:ari_max:20180814235229p:plain

 

DCGAN

GANではどのようなネットワークを構築すればキレイな画像が生成されるかまでは言及されていませんでした。DCGAN(Deep Convolutional Generative Adversarial Networks)では、以下の指針を出しています。

  •  Batch Normalization を使う(最も重要)
  •  Discriminatorでは pooling ではなく stride=2 の畳み込みにする(poolingを使うとぼやける)
  •  Generatorではdeconvolutionを使う(convolutionで画像を大きくする)
  •  全結合層をなくしてGlobal Average Pooling(Pooling時に全体の平均をとる)にする
  •  DiscriminatorではLeaky ReLU (α=0.2) を使う
  •  Generatorでは基本的にReLUを使い、出力層だけtanhを使う

で、ここからが本題。

強化学習

 教師データは明確でないが、最終的な目標だけが設定されているようなタスクです。例えば、将棋など1手がいいのか悪いのかという正解が与えられないが、最終的なゴールだけは明確なものを指します。AlphaGoなどは強化学習を使っています。

 

基本的な流れ

以下のことを延々と繰り返して、方策を学習していきます。(強化学習は時間がかかる)

  • 最初はランダムに行動する。
  • どの行動をとるかは「過去どんな状態だったか」を入力として導き出す。(方策という)
  • 行動を何回か取っていくと、報酬が手に入る
  • 報酬がプラスだったら「今までの行動はよかった」と学習し、マイナスだったら「今までの行動は悪かった」と学習する

エージェントというソフトウエアが環境ないで観察に基づいて行動をとり、それと引き換えに報酬を受け取ります。うまく行動すれば報酬がもらえて、うまく行動できない場合は罰を食らう、ということを繰り返してだんだん学習していきます。

 

f:id:ari_max:20180815071404p:plain

 実際のところ、深層強化学習はキビシイ(by Google Brain Robotics team)のです。

Reward Hackingが起きやすく、報酬を得るために望まない行動をしてしまうからです。(ラクしてしまう)

 

探索と利用のトレードオフ

「今まで学習してきた中で一番いい方法」だけ取っていてもより良い解は得られないし、「これまでやったことがない方法」だけ取っていても学習の意味がありません。

これらのトレードオフを考慮して、より良い方策を短時間で見つけることが重要です。

 

強化学習における2つのタイプ

強化学習には多くの種類があるのですが、大きく分けて価値ベースの考え方と方策ベースの考え方という2つのタイプがあります。

価値ベースの方法

ある状態がどれぐらい良いか(例:盤面の状態がどれぐらい優勢か)ということをベースにする方法で、より良い状態にするための行動をします。価値反復法がこれに当たります。

方策ベースの方法

状態は関係なく、方策自体を丸ごとモデル化する方法です。

価値反復法

行動価値

行動価値関数Q(s,a)で表す。状態sの時行動aをとると、どれぐらいの価値が見込めそうかを算出します。

状態価値

状態価値関数V(s)で表す。今の状態sは将来にわたってどれぐらいの価値が見込めそうかを算出します。

今取るべき行動が何かを知りたいという意味で、aが入っているQの方が扱いやすいので、Qをベースに考えていきます。

Q学習

強化学習の中で一番有名なもので、初期状態で確率の遷移と報酬がわからない時に合わせて価値反復アルゴリズムを修正したものです。

 

f:id:ari_max:20180816062044p:plain

 

 期待していた報酬よりも多かった場合はQ(s,a)の値を大きくし、少ない場合は小さくしていくことで、報酬の予想と実際の値が一致してきます。

問題点としては、状態が多すぎるということです。将棋であれば10の220乗あるので、メモリに乗り切らないです。近似するためにニューラルネットワークを使いますが、以下のような工夫が必要です。

Experience Replay

強化学習は時系列データになることが多いのですが、データ間の相関が強いです。(相関が強いとうまくいかない)一旦、状態・行動・報酬を全て保存してそこからランダムにサンプリングして利用します。(将棋であれば、盤の状態、手、その後の情勢を保存しておく。)Q学習と相性がいいです。

報酬クリッピング

強化学習は報酬が非常に重要と言われてきましたが、現実的にはうまくいきませんでした。(大差で勝てば+5点、僅差なら+1点など)

そこで、「良いなら+1」「悪いなら-1」と固定してしまうというのが報酬クリッピングです。調整の余地はなくなりますが、人間の介入がない分学習自体はうまくいくようになります。 

Fixed Target

Q値は見込みの値であり毎回変わってしまうので、教師データとして使うには不安定なものです。そこで、ミニバッチを抽出して期待値の計算をしている間だけは重みを固定しておくという方法です。

 

 方策勾配法

 価値ではなく、方策そのものを直接モデル化したものです。AlphaGoでもこの方法が取り入れられています。が、ニューラルネットワークで方策勾配法を学習すのはかなり難しいです。AlphaGoで提案されたテクニックは以下の通りです。

  • Batch Normalization は必須
  • ResNetを使うとよい
  • L2正則化を使うとよい
  • πとVを1つのネットワークで表現し、πは交差エントロピー、Vは平均二乗誤差(MSE)で、2つの和を損失関数にする 

YOLO(オブジェクト認識)

今回は、YOLOというリアルタイムでオブジェクトを認識するフレームワークを使ってみました。

pjreddie.com

実際のソースはこちら

github.com

 

ではでは。

 

DeepLearning講座(第8回)を受けてきたよ

7/20(金)にDeepLearning講座(第8回)を受けて来ました。

この講座もついに大詰め。少しは賢くなれたかしら??

 コンテンツは以下の通りでした。

  • 7/14でやった問題の解説
  • 生成モデル、識別モデル

7/14でやった問題の解説

7/14にイベントがあり、そこでチームごとのコンペが行われました。コンペの解説を行って下さいました。

お題

 お題は「教師用に用意されたワインのデータをもとに、ワインが高級かそうでないかを予測する」というものでした。

データは↓にあるので、ご参考まで。

どうやって自分は分析したか

限られた時間でしたが、自分なりに解いてみました。

問題を見ると、これは「分類問題」で「機械学習を使う」ということはすぐにわかったので、RandomForestClassifierやGradientBoostingClassifierを使うのかなと思いました。そして、欠損があれば補完し(実際にはなかった)、パラメータの絞り込みを行いました。(実際には必要なかったかも)

教師用データから20%を検証用データとして切り出し、精度を確認したところRandomForestClassifierを使った時が一番精度が高かったので、それを採用しました。一応出力出来たところで、タイムアップ。「パラメータチューニングできてないなー、グループで話し合うかー」なんて思っていたのですが、なんと。。。

誰もできていませんでした(^_^;)

発表者を決めるときも私に救いを求める目が。。。なんの改善もできずに発表する羽目になりました。。(結果は・・・でした。なんとかaccuracyが90%超えててよかった。。。)

オリジナルのコードはこちら

講師の方からの解説

ありがたいことに、最初の時間を使って解説していただきました。

データの性質を見る
  • 順序などは数値と質が違うので要注意
  • データの分布に外れ値があるか
データの件数を見る
  • データの数が少ないので、DeepLearningは使えない
  • XGBoostやLightGBMといったライブラリを使うのが良さそう。(Logistic回帰を素直に使うだけでもaccuracy88%の精度は出る)
 パラメーターチューニング
  • ランダムサーチでチューニングする

これらを踏まえたコードは、、、書いてみます。

 

 生成モデルと識別モデル

ここからが本題。

識別モデル (今までやってきたこと)

データxがクラスyに属する確率P(y|x)をモデル化し、確率が一番高いものを求める。

(一番確率の高そうなクラスyに分類する)

生成モデル

データxがクラスyに属する確率P(y|x)をモデル化せずに、ベイズの定理を用いて以下のように書き直す。

 P(y|x) = (P(x|y)P(y) ) / P(x)

 

生成モデルは「データが何かの分布から生成されているのかを考えて、その分布を推定する」もの。

 

f:id:ari_max:20180803070558p:plain

 DeepLearningにおける生成モデル

 DeepLearningにおける生成モデルには2種類あります。

  • VAE
  • GAN

VAEの方が比較的学習しやすいですが、最近の主流はGANです。

VAE (Variational Auto Encoder)

学習データを与えると、その学習データに似ているデータを生成できるようになる。

Auto Encoderの仕組み

隠れ層だけニューロンが少ないというのが特徴です。情報が少なくなっているのに入力が復元できているというのは、優秀な次元削減ができているということです。

f:id:ari_max:20180803074203p:plain

 

 

 うーん、間に合わなかった。

とりあえず公開だけしておきまーす。

 

 

 

 

 

 

 

DeepLearning講座(第7回)を受けてきたよ

7/6(金)にDeepLearning講座の第7回目を受けてきました。

今日の内容はEncoder Decoderの実装とLSTMによる文書分類のアーキテクチャでした。

コードを実際に書いている時間が長かったです。

Encoder Decoder Model

 Encoder部分とDecoder部分に分けて処理をすること。アルゴリズムではなく考え方。

2つの部分に分けることで、いろいろなことが出来ます。(例:画像→文章)

 

seq2seq

Encoder Decoder Modelの一つ。下の図のように英語(単語)を入力して、フランス語(単語)を出力します。EncoderもDecoderも可変長にすることが可能です。

f:id:ari_max:20180705070709p:plain

seq2seq

入力は単語単位でなくてはならないの?

文字単位に入力しても構わないが、単語の概念を学習する必要があります。('a' の後に'm'が入力されたら'am'だよね。というように区切りを学習する)ただし、日本語のように単語と単語をスペースで区切らない言語は難しいと言えます。

単語で区切った方がいいのか、それとも1文字ずつ区切った方がいいのかは議論になっているそうです。

実装のポイント

  1. 入力値は固定次元にする(プログラムとしてやりにくい)
  2. Encoderは普通のLSTM
  3. Decoderはちょっとややこしい(RepeatVector,TimeDistributed,ReturnSequence)
  • ReturnSequence→trueにすると全ての結果が出てくる
  • RepeatVector→出力されたものを入力として使ってくれる
  • TimeDistributed→同じネットワークを全てに適用してくれる

 処理フロー

Encode
  1. 入力値はそのままでは扱えないので、数字に変換する
  2. 単語の類似度を反映した単語ベクトルに変換する(Kerasだと、Embedという層を入れると勝手にやってくれる)
  3. 単語ベクトルから内部ベクトルに変換する
Decode
  1. "ここからがDecoderですよ(GO)"というフラグが来たら、Decodeを開始する
  2. 内部ベクトルから単語ベクトルに変換
  3. 変換したベクトルとEncodeから変換した単語ベクトルに一番近い出力値を選ぶ
  4. 出力値を単語ベクトルから変換して、出力する

コードをKerasで書くと、こんな感じ。

EncoderはシンプルにLSTM。

# Encoder
model.add(LSTM(n_hidden, input_shape=(input_digits, n_in)))

Decoderはちょっと複雑。ここではRepeatVectorを指定

# Decoder
model.add(RepeatVector(output_digits))
model.add(LSTM(n_hidden, return_sequences = True))
model.add(TimeDistributed(Dense(n_out)))

モデルの学習

# モデルの学習
model.compile(loss='categorical_crossentropy',optimizer=Adam(lr=0.001,beta_1=0.9,beta_2=0.999),metrics=['accuracy'])
h = model.fit(X_train, Y_train, batch_size=batch_size, epochs=epochs, validation_data=(X_validation, Y_validation))

モデルを使って予測

prediction = model.predict_classes(question, verbose=0)

次回からはGitHubにコードあげようかな。

DeepLearning講座(第6回)を受けてきたよ

6/22にDeepLearning講座の第6回目を受けてきました。

今日の内容は時系列処理(GRU)の実装とSeq2Seqの実装でした。なかなか難しいのですが、丁寧にアウトプットしていこうと思います。

GRU

GRUとは

GRUとはLSTMを簡略化したもので、メモリセルを廃止し入力ゲートと出力ゲートを一つにまとめたもの。その代わり、更新ゲートとリセットゲートがあります。

  •  更新ゲート:過去の情報をどれだけ取り込むかを決定する
  • リセットゲート:過去の情報をどれだけ捨てるかを決定する

f:id:ari_max:20180705064637p:plain

更新ゲートとリセットゲート


 

以下のステップで算出します。

  • 更新ゲートの値を計算する
  • リセットゲートの値を計算する
  • 保持している過去の入力値に対してリセットゲートの値を反映させる(忘れさせる)
  • 未来に使用するための入力値を計算
 更新ゲートの計算

 更新ゲートの式は

(入力値×重み +保持している過去の入力値×更新ゲート向けの重み) × sigmoidです。

f:id:ari_max:20180704122200p:plain

更新ゲートの式
リセットゲートの計算

一方、リセットゲートの式は

(入力値×重み + 保持している過去の入力値×リセットゲート向けの重み) × sigmoidです。

f:id:ari_max:20180704122157p:plain

リセットゲートの式
保持している過去の入力値に対してリセットゲートの値を反映

そして、リセットゲートの値で「保持している過去の入力値」を更新します。式は

「入力値×入力値の重み」 + 「リセットゲートと保持している過去の入力値×リセットゲート向けの重みを要素ごとに掛け算したもの」に対してtanh(活性化関数)を掛けたものです。

f:id:ari_max:20180704122152p:plain
保持している過去の入力値を更新
未来に使用するための入力値を計算

最後に、未来で使用するための入力値を計算します。「どれぐらい記憶を更新するか」という計算をするわけです。更新ゲートが1に近ければ近いほどリセットゲートを反映した過去の値を無視して更新することになります。

式は「更新ゲートと過去の入力値を要素ごとに掛け算したもの」と「(1ー更新ゲート)と『リセットゲートで更新した過去の入力値』を要素ごとに掛け算したもの」です。

 

f:id:ari_max:20180704122640p:plain
未来に使用するための入力値を計算

 

式で書くと非常に難しいですが、TensorFlowだと

tf.contrib.rnn.GRUCell(num_units=n_neurons)で済んでしまいます。。。 

Seq2Seq

Seq2Seqは「シーケンストゥシーケンス」といいます。Encoder-Decoder Modelの一種で、可変長文字列を可変長文字列に変換することができます。下の図のように翻訳などに使われるものです。

f:id:ari_max:20180705070709p:plain

seq2seq

Encoderでは入力を処理して最終的に一つのベクトル(人間には分からない形式)にし、Decoderでベクトルから復号し単語を出力しています。

※英仏翻訳の場合、512次元のベクトルを使用しています。(強力!)