[Meisyo] 探索

これは何?

名将と呼ばれた者達の1機能である探索(2020年4月実装)について書いた記事です。

実装目的

1・別目的の追加練習を追加したい
 これまでは選手の獲得・育成をメインにした追加練習しかなかったためです。
 探索は、アイテムの獲得およびNPC戦(実装予定)が主な目的で、ある程度選手が育った中級者以上向けのコンテンツです。

2・動的なプレイ体験をしてもらいたい
 限られた時間の中で、自分の操作次第で結果が変わりうるコンテンツが少なかったためです。
 探索は、さまざまな地形による影響を受けながら、アイテムをいかに早く・多く集めるか、ボス高校(NPC)を見つけるかが重要になってきます。
 UIはスマートフォン向けで、直観的に扱えるように工夫しています。操作方法はクリックのみです。

遊び方

少しUIが変更されている部分がありますが、基本的に遊び方は同じです。

1. ロードする

2. 探す

操作はクリックのみ。
画面下にある薄黒色のタッチパネルで、キャラクターの移動する向きを決めます。

3. 探す

地形により侵入不可、HP減少加速、滑りやすいなど効果があります。

・ボスNPC戦
 チケット等を使わずにボスNPC(Meisyoオリジナル)と試合を行うことができます。
 1度試合すると24時間独占挑戦権が得られ、他人には挑戦できなくなります。
 勝利すると、覚醒用アイテムなどが獲得可能。
 詳細はこちら
 イベントなどに使う予定です。

4. 手に入れる

気合の塊をはじめ、その他レアアイテムが多数出現します。
誰も探していないエリアはアイテムの宝庫になる調整を行っています。
※NPC戦を行うとその回の追加練習(探索)は終了されます。

[Meisyo] アップデート予定のお知らせ

名将と呼ばれた者達、今回のアップデートではバランス調整をメインに行います。

まずは謝辞として、Meisyo アンケート 2020/02にお答えいただき誠にありがとうございました。また、ゲーム内でバグなどのご連絡いただきありがとうございました。アップデートの参考にさせていただきました。

今回のアップデートもアンケートから採用させていただいた項目が何個もあります。
方針は基本的に、データ野球の楽しさを多くの人に、より深く楽しんでもらえることを目指しています。
(反対に、プレイする野球だとそもそも他ゲーで良くね?となります。)

まとめたもの → 野球ゲームPJ まとめ

アップデート予定概要

・連盟戦の選手自動登録機能追加
 連盟戦で参加した人が自動で選手登録を行う機能を追加します。
 人数不足で何名か参加できていない人が確認できたためです。
 
・選手枠の追加
 50→100に増加。ただし、追加の50枠は休眠扱いで練習等で経験値が入らない。
 そういった制限を採用するのは、単純に枠を増やすとカードを多く持てるチームが育成上有利になるためです。

今後の予定概要

・探索の追加
 選手の体力を使いながら、時間制限のダンジョンで選手経験値とアイテムを探す。
 たまに投手or野手と対戦して、規定以上なら経験値&特別アイテム、ある条件を満たすと選手を獲得できる。

・新シナリオの追加
 春の選抜甲子園大会(3/19~)を再現。

・選手画像の追加・変更
 Meisyo独自の選手アイコンを作成する。
 パワポケなどではなくて、独自IPにしたい。

・選手属性追加
 苦手な選手・得意な選手がわかりやすいよう緑、青、赤の3属性を追加。
 既存の隠しパラメータ(投球モーションや打撃フォーム、調子2など)は表示・変更されない。
 (選手層がある程度厚くないとマイナス補正は厳しいか。)

駆け出しエンジニアは本当にフロントエンドエンジニアを目指すべきなのか

最近、駆け出しエンジニアをTwitter上で数多く見るようになりました。
ただ、揃いも揃って「フロントエンドエンジニア」になりたい(意訳)とプロフィールに書いてあります。

なぜでしょうか。
それは何が問題なのでしょうか。

私が何を問題視しているかというと、フロントエンドエンジニアってそんなにコストパフォーマンス良くないぞ?ということです。
なぜなら、バックエンド等に比べて要求される技術の種類が多くなりがちであるからです。

そこで今回、給与や技術などの視点から、この原因を読み解いてみました。
※すべてのベースとなる情報科学の知識については全く触れていません。

目次

給与
技術
目指してしまう理由
解決策
あとがき

給与


indeedをはじめ、リクナビネクスト、マイナビ転職、Green等のサイトを確認すると、その他のエンジニアに比べ、給与が10~20%程度低めです。

・フロントエンドエンジニア

・バックエンドエンジニア

*フロントの20%増しくらいの給与水準でした。
* 同じ待遇にしている会社も確認できました。内部で違っているのかもしれません。

エンジニア職種には数多く存在し、組み込み系やパッケージ系、社内SEもあればインフラ、品質管理等々が存在します。
その中で高いわけでもなく、低めの水準でした。

技術


フロントエンドエンジニアで特に問題なのは、習得すべき技術の種類の多さです。

基本的な技術だけでも大きな差が付きます。

・フロントエンドエンジニア
HTML(表示基礎)
CSS(表示スタイル調整)
JavaScript(動的なページの作成)

・バックエンドエンジニア
PHP(システム基礎:Ruby、Java、JavaScript等も可能)
SQL(データベースとの接続:AWS、BigQuery、RedShift等も可能)

1種類しか違わないのでは?と思いがちですが…、

HTML、CSS、JavaScriptは全く構文が違います。
例示していきます。

HTML

<タグ>~</タグ>を基本とした構成です。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>タイトル</title>
...

CSS

HTMLで作成したタグやクラス、idごとに表示スタイルの調整を行います。
タグ名 { ~ }です。

/** General Style Info **/
body {
	background: #003d4c;
	color: #fff;
	font-family:'lucida grande',verdana,helvetica,arial,sans-serif;
	font-size:90%;
	margin: 0;
}
a {
	color: #003d4c;
	text-decoration: underline;
	font-weight: bold;
}
...

JavaScript

動的なページを作るために、訪問者の属性ごとなどで条件分岐をさせたり、文字や背景自体を動かしたりできます。
下記はJQueryという過去のライブラリの記法です。

jQuery(document).ready(function( $ ) {

  $('#myTab a').click(function (e) {
    e.preventDefault()
    $(this).tab('show')
  });

  $('.carousel').carousel();
  
  $('#accordion').collapse({toggle: false});
...

PHP

PHPなどは比較的書きやすい言語です。
基本的な書き方はその他の言語(Ruby、Java、Python等)も同じです。
下記はCakePHPというフレームワークの記法です。

    public function LoadCSV($file, array $ycol, array $xcol, $hasHeader=true)
    {
        $xarray = array();
        $yarray = array();
        $handle = fopen($file, "r");

        //if first row has headers.. ignore
        if ($hasHeader)
            $data = fgetcsv($handle);
        //get the data into array
        while (($data = fgetcsv($handle)) !== FALSE)
        {
            $rawData[] = array($data);
        }
        $sampleSize = count($rawData);

        $r = 0;
        while ($r < $sampleSize)
        {
            $xarray[] = $this->GetArray($rawData, $xcol, $r, true);
            $yarray[] = $this->GetArray($rawData, $ycol, $r);   //y always has 1 col!
            $r++;
        }
        $this->x = $xarray;
        $this->y = $yarray;
    }
...

SQL

SQLは最も安定した言語です。
プログラミング言語にしては珍しく、ほぼ改修が入らず学んだことがずっと活かせる言語です。

SELECT
  date,
  COUNT(user_id) AS count_uu
FROM
  action
GROUP BY
  1
ORDER BY
  1

このように、HTML、CSS、JavaScriptは全く構文が違います。
それだけで理解するのが大変だと思います。しかも、実務では構文だけではなく、情報科学のロジックもふんだんに使います。

その他に、より問題だと考えている事柄としては、HTML、CSS、JavaScriptはすべて、技術の移り変わりが激しいことです。

新しい記法や、非推奨とされる記法、さらには動かなくなる記法すら出てきます。
新しいライブラリ、フレームワークと盛りだくさん追加されていきます。

JavaScriptのフレームワークとPHPのフレームワークで種類の数が全く違います。

例:JavaScriptのフレームワーク
AngularJS
Hyperapp
jQuery
React.js
Riot.js
Vue.js

その他にNode.js、Nuxt.js等も・・・それだけ、フロントエンド側で求められていることが多いということですね。よく調べてみると、フロントエンドのフレームワークにも関わらず、バックエンドの知識を当たり前に要求するものがあります。

例:PHPのフレームワーク
Laravel
CakePHP
zend framework
Symfony
FuelPHP

JavaScriptは見る限り、Angular、jQuery、React、Vueは現役です。
PHPもフレームワーク数は同等くらいですが、CakePHPがメインでLaravelが増加してきており、メインで使われるフレームワークは決まっている状況です。

バックエンドエンジニアですと、SQLは安定しているため、学習し続けるべきなのはメイン言語+αで済みます。
フロントエンドエンジニアは、進化し続けるHTML、CSS、JavaScriptをはじめ各種フレームワークの学習が必須です。

ちなみに、フロントエンドエンジニアの宿命として、デザインや触り心地の言語化、直感等も必要になってきます。
サーバーサイドエンジニアはサーバーとの通信速度(アルゴリズムの適正さ)やバグの少なさ、コードを書く速度等で能力は定量化しやすいですが、フロントエンドエンジニアはコードだけではない能力が要求されます。

そこまで頑張って得るものは多くはありません。
確かに自分で0 → 1ができるのは大きな強みですが・・・。

私が言いたいのは、
なぜフロントエンドエンジニアを(メインで)やりたいのか?
その他のエンジニア職種では何がダメなのか?
ということを彼らが理解しているしているのだろうかということです。

現役のエンジニアやIT業界の人に聞けばいいのに、と。

目指してしまう理由

一番目:わかりやすさ

表示側を作るのは素人にもわかりやすいし、今風のカッコいいサイトが作れるようになりそうですよね。(実はそんなに簡単ではないですが・・・)
自慢できる職業だと思います。

二番目:情報発信の多さ

フロントエンドエンジニアは数の多さと認知度の高さから、職業に関する情報発信がされやすい傾向にあります。

組み込みエンジニアの情報発信を見たことありますか?

僕はあまりに見かけない印象です。
情報を必要としている人が少ないですし、目立つ職業ではないので内々の情報交換や技術書での学習が多いです。

QiitaやTeratailなどが人気なのも要因でしょう。
フロントエンド以外の技術は基本的に見ないか、原典(説明書)および論文が仕事への理解として一番早いです。

日本語のやってみた記事は数多くあるのですが、コードだけ(何を考えてこのコードを書いたか説明がない)だったり、理解せずに数式をなんとなく書いてある(使用上の注意点が書いていないことやミスリードしている)記事が散見されます。
* QiitaやTeratailは良いサイトです。使い方を間違えなければ。
* 記事が転職等の実績として利用されつつあることに起因するのかもしれません。

それらの発信を職業にしている人も存在します。
そうです。インフルエンサーです。

オンラインサロン
自社開発
フリーランス

どれもキラキラワードですね。
ただよく考えてほしいのは、元々エンジニアの人たちがなぜそうなっていないのか?ということです。
彼らには本当かどうかを現役のエンジニアやIT業界の人に聞けばいいのに、と思いますね。

知っている方が動画配信をしているので以下をどうぞ。私の認識とほぼ同じです。
エンジニアのキャリアハックを配信「IT人事の毛呂淳一朗」

三番目:日本の現状と転職市場の活性化

日本の平均年収は400万円台です。
ちなみに、平均値は上位の値に引っ張られやすいので実情とは異なります。

例)4人(300万、400万、500万、1000万円)の平均年収
 ⇒ 550万円!??
一人が異常に高ければ、すぐに上がります。
それだと実感と合いませんよね。

実感に近い値としては、中央値で約350万円です。それ以下の人が約50%も居るのですが…。

IT業界は東京に一極集中していることもあり、ジュニア(駆け出し)レベルでも350万~が基本です。

・・・あれ?中央値を超えてない?

超えています。そういうことです。

つまり、低い年収をどうにかしたい人たちの入り口になっています。
それで駆け出しの方が多くなっているのかなと考えました。
(私はこの流れ自体は問題とは思っておらず、正常だと思っています。新しい産業にもっと人が移動すればいいとも思っています。)

ちなみに東京近辺は家賃以外は全然物価は変わらないです。

家賃は東京23区>東京郊外>千葉・さいたま等です。
住所を東京から千葉にすれば、ほぼ条件が同じような場所かつ駅にも近いのにも関わらず3~4万家賃が安かったです。

物価の面でいうと、東京・千葉は地方(例:奈良(実家)、栃木(前職)、熊本(大学・大学院))はほぼ変わりません。
野菜や地域の名産品が安いかな、でもそんなに変わらないかなという状態です。

ただし、東京は外食だけは高い・・・。ランチだと1,000円オーバーが当たり前でちょっと面食いました。
家賃が高いので仕方がないのですが。

解決策


複数の現役のエンジニアやIT業界の人に聞く、つまり一人だけを信頼するなという二点さえ注意すればよいです。

よくあるのは、
・給料が良さそうなので
・まともな労働環境で働きたかった
・友人に勧められて

だから、フロントエンドエンジニア・・・というのは少し安直ではないでしょうか。

なぜフロントエンドエンジニアをやりたいのか?
その他のエンジニア職種では何がダメなのか?
という問いは必ず付きまといますし、転職した後にもついてきます。

あなたが納得した道を選んでほしい。
その思いでこの記事を書きました。

あとがき


著者も29歳で製薬企業研究開発職からIT系職種(データ分析コンサルタント:機械学習や統計を企業の事業・業務方針コンサルティングに活かす)に転職した当人として、今の流れは危険だなーと思い記事を書きました。

もちろん、働いている場所によって定義が少しずつ異なるので、「あれ、これは書いてあったことと違うぞ?」と思うかもしれません。
バックエンドの方は内容によって複数言語を扱うのが基本だったり、サーバー側で突発的かつ大規模なアクセス増加に対応するためのクラウドであるAWSまたはGCPなどの知識が必要だったりと…。
実際はもっと複雑な問題なのですが、理解の簡便化のために問題を切り分けてお伝えしています。

元々趣味でプログラミング・HP作成をやっていたこともあり、フロント(HTML、CSS、JavaScript(JQuery他))もバック(PHP、Python、Perl、C系、MySQL)もインフラ(Linux)もちょっとはできる程度でした。

そういった経歴もあり、フロントエンドエンジニアも少し考えていた時もありましたが、上記懸念(技術更新早すぎやろ)もあり、デザイン系のセンスもないしでやめておきました。
昔は「JavaScriptはクソ」という扱いが基本でした・・・私は使ってましたけど。・・・それくらい大きな変動が10年以内でありました。

あなたは何がしたいですか?

どんなことに興味がありますか?(または、どんなことが許せませんか?)

カッコいいことに引っ張られていませんか?

これは自戒を込めて書きました。
私は今流行りのデータサイエンティストと名乗れます。でも、それは間違っていると思います。
実際の仕事は機械学習、統計解析の手法「も」使いますが、ビジネスサイド寄りです。
職種名はビジネスアナリストが非常に近いです。
エンジニアの方とも、データサイエンティストの方とも、コンサルタントの方とも働いているので、少しは目端が効くようになってきました。

・お客様にいかに現状を認識していただくか
・どこに事業の問題点があるか
・どうすればユーザーの苦しみを解決でき、事業が伸びるか

この3点を一緒に考えて、並走しています。
以前取れなかった(らしい)契約継続もしていただきましたし、満足度も高いのかなと思います。
事業を良くしていくことが非常に面白いですし、誇りに思っています。(給与はまだ安いけどね)

そんな職業に一人でも多くの人が会えるよう祈っています。
ちなみにTwitter > R(あいれい/おいす)で相談していただければいつでも対応します。
プログラミングの基礎をはじめ、統計や機械学習の手法や就活等もなんでも相談してください。
ただ、一人だけを信頼するなは気にかけておいてください。

それでは!

おすすめ記事

この記事を読んだ方には、下記の記事が読まれています。

未経験がエンジニアを目指すことって、それって意味あんの?

決定木分析(Python CHAID)を解釈する

意思決定のために使用される決定木分析

Scikit-learnでの決定木にはCART(指標:giniまたはentropy)他が採用されています。
CARTは下記の2点を含め、さまざまな理由から使われていません。

  1. 2分岐のみのため単純すぎる
  2. 過学習させるアルゴリズムのため、小さすぎる区分が出現するので扱いにくい

コードは以下の通り非常に簡単です。

from sklearn import tree
clf = tree.DecisionTreeClassifier(max_depth=3)

(参考:scikit-learn で決定木分析 (CART 法))

実務では各種BIでのCHAID(指標:カイ二乗値)が使われています。
利点としては、以下の2点が大きいです。

  1. 多分岐
  2. 過学習する前に学習を止める

Pythonには決定木(CHAID)の一般的なライブラリがないので、今回はライブラリCHAIDを紹介します。

概要

下記のツイートにまとめています。
まずはREADMEを読んでCHAIDをインストールしてください。
GraphvizとOrcaのインストールは項目Exporting the treeをご覧ください。

Graphvizインストールできない問題の対策

Windows10の場合、pipではなくcondaで入れるとうまく行きました。

conda install graphviz
conda install python-graphviz

Graphvizのbinとdot.exeまでの環境パスは通しました。

CHAIDテストコードにCSVを読み込ませる方法

下記コードで対応可能です。

df = pd.read_csv('sample.csv')

col_answer = 'flag_keizoku'
ndarr = df.drop(columns=col_answer).values
arr = df[col_answer].values

検証

CHAIDとBIツール(今回はSPSS)との差分を検証しました。

検証方法

実データから決定木を出力してみる。

説明変数

ある2週間(1-14日)にログインしたユーザーの総ログイン日数
*開始日を1日目としてカウント
*値=1~14

目的変数

ユーザーごとの15-28日目の継続有無
*ログインしたかどうかで判別
*値=0 or 1

結果概要

SPSSだとある程度グループ化されるのでしきい値の判断はしやすいが、CHAIDはグループ化されず数値が前後してしまうため解釈が難しい。
解釈のために使っているのに、解釈が難しいとは・・・使えないやんって話です。

結果詳細

SPSS


グループは7個できました。
[1], [2], [3-5], [6-8], [9-11], [12, 13], [14]

CHAID


グループは11個できました。
[1], [2], [3], [4], [5, 7], [6, 8], [9, 10], [11], [12], [13], [14]
*個体数nが描写されない問題。コードでの確認は可能ですが、可視化したグラフとしては使いにくい。

結果解釈

前の2週間のログイン日数が多いほど、次の2週間でいずれかの日にログインしている可能性が高い。

うん、そうだね・・・(当たり前)としか言えないのですが、どれくらいの日数ログインしていないと継続しにくいか?を知るには良い結果となります。

14日ログインしている人のうち、3.8%がログインしてないってまずくない?という感じで解釈していきます。

その他には、時系列で比較することで、継続率の効果検証を行うことができます。

仮説:継続を構成するユーザーのうち、どの層のユーザーの継続率が上がったのか?
→ 全体的に上がったのか?
→ 特定のユーザー層にのみ効果があったのか?

のように細かく調査することができます。

このCHAIDの何が問題か

決定木分析はあくまで、いい感じで区分を作り、その結果を解釈して施策に繋げるために行っているので、グループ化があまりに細かく、理解しにくいモノだと使えません。

SPSSに比べ、CHAIDのカイ二乗値は高いですが、適切なグループ化をしていないので、人が使うためには、このCHAIDはめちゃくちゃわかりにくい。

結局、意思決定のために分析をしているのに、読み取れない…のようなことが起こりえます。

そういった懸念点があっても、このCHAIDは無料ですので、コスト面を考えると使う価値はありますね。SPSSはライセンス料がかなーり高いので、個人で要因分析する分にはCHAIDで問題ないでしょう。

[社内コンペ] 細胞画像認識

これは何?

某社で行われている社内コンペのメモです。私の備忘録でもあります。

結果:2位
精度:68.3%
120枚をクラス1~3で各40枚としていました。
ただし、その数値に合わせに行こうとすることはしませんでした。

今回の目的変数

「画像が何に種別されるか」の3値分類問題です。

回答は、
・通常細胞
・変性細胞
・変性細胞(悪化型)
とよくある形です。

今回の評価指標・ベースレート

Accuracy(精度)です。

出題者によると、お試しの意味も込めてAccuracyだそうです。
普通なら複合指標による評価が妥当とされています。
Precision(適合率:真の陽性をいかに漏れなく見分けられたか)やAUC(認識性能の高低を表す)、MAP(Mean Average Precision、Precisionの応用)などなど。

ベースレートはaccuracy60%です。

僕は、検証の際はAUCを用いていました。
他のコンペでよくある、「指標はRMSE、ただMAEで評価を行ったほうが結果が良くなる」という事例を途中で採用しました。

今回の説明変数の特徴

画像

訓練に使用できるのは1,200画像

 極度に少ないです。
 通常10万~とある場合が多いのですが、今回はこの制約下で行います。
 ただし、カラー写真ですので染色等での色の変化(非常に重要)は確認できます。
 なぜ色の変化が必要かというと、見た限りでは核を染色しているし、その他にも染色処置をしていたためです。
 変性細胞には核の形態変化が確認でき、その形態変化をDNN(Deep Neural Network)が捉えるには色が必要ではないか?ということです。
 ※本来は詳細情報を当該領域の研究者(顧客の会社所属)に確認したかったのですが…不可能だったことが残念です。
 ※このようなドメイン知識は作成する上で必要になります。

見分けるのには訓練が必要

 画像を「変性細胞である」とは見てわかるのですが、「悪化している」かはわかりませんでした。
 * 一応生化学系の修士 → 製薬会社(感染症系)に居たのである程度はわかりましたが…他の人はわからないそうです。
 悪化判断ために、本を実家から取り寄せ&知り合いのおすすめを新規購入しました。
 結局、変性細胞の種類が多すぎて判断できなかった…。
 ※特に細胞診はどの部位であるかが重要です。そもそもの細胞の形状が違います。今回はそのラベリングは存在せず、3クラスラベルしかなかったので、「大丈夫かコレ?」とはなっていました。

分け方が雑

 画像を確認したところ、採取部位別にデータがまとまっているわけではないようでした。
 なぜなら通常細胞の形がそもそも違うとひと目でわかりました。

 一般的に組織ごとに細胞の形は異なりますし、それらが変化した変性細胞も見た目が異なります。細胞への症状としては、核の膨張や(免疫機構の攻撃による)細胞の生死が確認できました。

 問題としては画像の中心に変性細胞がなかったり、写っている細胞数がごく少なかったり、同じ細胞を回転させただけで撮っている可能性がある写真も確認できました。

今回の目標

コピペしない。
画像認識の良い解決法を見つける。

ある程度個人で古典的な画像認識から深層学習を学んでいたので、どんな手法があるかなどは少し分かる状態でした。
Kerasなどの説明書をメインに、解説書(Kaggle等のDiscussion含む)、論文を見てモデルを構築します。

今回行った作業の流れ

画像を見て、前処理を考える

当たり前ですが、最初に画像を確認しました。
難しいポイントは何かアタリを付けましょう。
今回はフィルタ等は使用しませんでした。
※ノイズを取り除くにはいろいろなフィルタ(ガウシアン、バイラテラル、プリューウィット、LoG等)があります。

listにopen_cvで読んだ画像データを入れて、以下のような処理を行いました。

def image_preprosessed(img):
    # ノイズ除去
    # https://qiita.com/icoxfog417/items/53e61496ad980c41a08e
    # https://www.slideshare.net/takahirokubo7792/ss-71453093
    #img = cv2.GaussianBlur(img, (1,1), 0)
    return img

※実際は処理を行っていない。

自作Sequentialモデルを作成

よくあるConvを重ねただけの単純なモデルを作成しました。(精度30%)

モデルの定義

def model_base(model):
    model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(image_scale, image_scale, 3)))
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Conv2D(64, (3, 3), activation='relu'))
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Conv2D(128, (3, 3), activation='relu'))
    model.add(layers.MaxPooling2D((2, 2)))
    model.add(layers.Conv2D(128, (3, 3), activation='relu'))
    model.add(layers.GlobalAveragePooling2D())
    model.add(layers.Dropout(0.5))
    return model

Dense層を置いていないのは、GlobalAveragePooling(GAP)のほうが、
・精度が良く
・パラメータが少なくなるので計算が早い

という性質があるからです。(詳細:https://arxiv.org/pdf/1312.4400.pdf)

GAPからGlobalMaxPoolingの置換も効果ありそう(精度+0.5%)ということは分かりました。

モデルの実装

model_no1 = models.Sequential()
model_no1 = model_base(model_no1)
model_no1.add(layers.Dense(n_categories, activation='softmax'))
model_no1.compile(
    loss='categorical_crossentropy',
    optimizer=optimizers.Adam(lr=1e-4),
    metrics=['categorical_accuracy']
)

追加でmerticsにkerasのAUCを導入していました。
optimizer(最適化関数)も結局はAdamではなく、SGDでした。

最適化関数のSGDは学習は遅くなるものの、Adamのように局所解に落ち込みにくいので、結果精度が高くなる傾向にあります。
DNNは(数学的に見ると)学習曲面上で局所解が多いらしい(詳細は理解できなかった…)ので、精度を上げるためには問題に合った最適な最適化関数を選択する必要があります。

学習済みモデルのファインチューニング(1)

よくあるVGG16をblock5_conv1層付近でファインチューニングしました。(精度50%)

モデルの定義

base_VGG16 = VGG16(weights='imagenet', include_top=False, input_shape=(image_scale, image_scale, 3))

学習層の定義

base_VGG16.trainable = True
set_trainable = False
for layer in base_VGG16.layers:
    if layer.name == 'block5_conv1': # この層以降は学習可能
        set_trainable = True
    if set_trainable:
        layer.trainable = True
    else:
        layer.trainable = False

書いてある通り、転移学習はある層以降の学習を可能にして行うことが多いです。
前半の層はさまざまな種類のフィルタ構造になっており(確認するには各層を可視化するとOK)、画像の各特徴量(角、線、円等)を自動的に抽出する働きがあります。
後半の層は、事前学習用のモデルに合わせた重みになっているので、適用したい問題に合わせた重みに変化させます。

そのため、どの層から学習させるかにも検討すること必要です。
調べている限り、Batch Normalizationのみ学習可能にさせるなど、色々方法がありそうです。
とはいえ、VGG16は全く検証ができなかったことが残念です。
元々の精度が低かったので放置しました。

学習済みモデルのファインチューニング(2)

Xceptionを108層以降ファインチューニングしました。

モデルの定義

base_XP = Xception(weights='imagenet', include_top=False, input_shape=(image_scale, image_scale, 3))

※include_topをTrueにすると、元々の画像サイズにTrain画像を合わせないと動かなくなります。なお、それぞれのモデルで画像サイズが違う問題があるので、input_shapeで設定しています。

学習層の定義

base_XP.trainable = True
for i, layer in enumerate(base_XP.layers):
    if i <= 108:
        # BNだけTrueにする
        if layer.name.startswith('batch_normalization') or layer.name.endswith('bn'):
            layer.trainable = True
            print("◆%s\r\n %s is Trainable:%s" % (i, layer.name, layer.trainable))
        else:
            layer.trainable = False
            print("◆%s\r\n %s is Trainable:%s" % (i, layer.name, layer.trainable))
    else:
        layer.trainable = True
        print("◆%s\r\n %s is Trainable:%s" % (i, layer.name, layer.trainable))

Trainすべき層が問題に合ってるのかどうか甚だ疑問です。
ただ、こちらで一番上手く行ったので、ここに記載させていただきました。

学習済みモデルのファインチューニング(3)

InceptionResNetV2をblock8_10_conv層以降ファインチューニングしました。
Kerasで導入可能なモデルは、https://keras.io/ja/applications/を参考にしてください。

モデルの定義

base_IRN2 = InceptionResNetV2(weights='imagenet', include_top=False, input_shape=(image_scale, image_scale, 3))

学習層の定義

base_IRN2.trainable = True
set_trainable = False
for layer in base_IRN2.layers:
    if layer.name == 'block8_10_conv': # 要検討
        set_trainable = True
    if set_trainable:
        layer.trainable = True
    else:
        layer.trainable = False

結果としては、Xceptionとほぼ同等でした。
ただし、このモデルだけPseudo Labelingを行うと精度が下がる問題を抱えていました。

評価指標の変化を確認

ここまでで4モデルを作成しました。
・自作簡易モデル
・VGG16ファインチューニング
・Xceptionファインチューニング
・InceptionResNetV2(IRNV2)ファインチューニング

それぞれ、TrainのLoss(損失関数)とAccuracy(精度)、AUCを、Testからどれくらい離れていたかを確認しました。
K-Foldクロスバリデーション(K=5~10)も試しましたが、学習・検証に時間かかりすぎたので途中でホールドアウト法に戻しました。(良くないことです)
ホールドアウト法でも問題ないデータ量(n>10,000)なら良いのですが…

基本的にファインチューニングを行うようなモデルは表現力が高い(意訳:色々と細かいところまで学習できる)がゆえに過学習しやすいので、Testと回答の精度が離れがちです。
そのため、どのモデルがTestで見える見た目の精度と回答の精度が近いか確認しておきます。

・結果
基本、XceptionとIRNV2は精度も高く適正な値を保ちやすかったです。DropOutを高い値(0.7付近)に持っていたことも要因と考えています。
VGG16は過学習しすぎで、自作簡易モデルはデータに対してモデルが小さすぎて学習できず…という結果でした。

検討事項

・予測に使うDense層の前にDropOut層の強さ(0.01~0.99)を検討すること
→ 特徴量がうまく生成できない場合、非常に高い(0.9)値で上手く行ったことがある。(らしい)

・画像数の水増し(Data Augmentation)
→ ImageGeneratorを使っていたので変更は簡単でしたが、意味を理解して使えてない気がしました。ここも要検討です。

# 機能追加
# https://towardsdatascience.com/image-augmentation-for-deep-learning-using-keras-and-histogram-equalization-9329f6ae5085
datagen_train = ImageDataGenerator(
    rescale=1./255,
    rotation_range=90, # 回転
    width_shift_range=0.9, # 横シフト
    height_shift_range=0.9, # 縦シフト
    zoom_range=0.4,
    horizontal_flip=True,
    vertical_flip=True,
    fill_mode='wrap', # abcd|abcd|abcd
)

→ ImageGeneratorを使う場合、メモリが足りなくなる時があります。その時はbatch_sizeを減らすと良いです。
※計算時間はかかるようになりますが、計算は止まらなくなります。

history = model_xp.fit_generator(
    datagen_train.flow(X_train, y_train, batch_size=10),
    epochs=100,
    validation_data=datagen_valid.flow(X_test, y_test, batch_size=10),
    steps_per_epoch=100,
    callbacks=[model_decay, model_check]
)

・LearningRateSchedulerの変更タイミング
→ 基本100epochsで学習を行っていたので、学習率を変更する(下げていく)タイミングをどうするか決めかねて適当に設定していました。

# 学習epochごとに学習率低減を実装(all100)
def step_decay(epoch):
    x = 1e-3
    if epoch >= 80: x = 1e-4
    if epoch >= 83: x = 1e-5
    if epoch >= 86: x = 1e-6
    if epoch >= 89: x = 1e-7
    if epoch >= 92: x = 1e-8
    if epoch >= 95: x = 1e-9
    return x

初期を1e-3にしている理由は、あまりにも学習率を上げると学習済みモデルの重みを参考にせず、壊してしまうことが起こるためです。
少しずつ計算を進め、最適解を探すようにしましょう。

・ModelCheckpointの設定
→ Test_AUC基準で取得していました。

・画像を大きくすると精度は上がるのか?(フルカラーの写真のため)
→ 少し大きくすると精度は上がりました。さらに大きくするとデータ量が爆発してメモリに乗らない…

出来なかったことは以下の通りです。
・モデル内での回答の重み付け
→ 変性細胞(悪化型)の識別精度が悪いこともあり、「変性細胞(悪化型)の回答の確率出力をさらに重み付けるような方法に変更してはどうか」ということを考えていました。
重み付けアンサンブルからの着想ですが、うまくいくのかどうかわかりません。

・アンサンブル
→ 結局XceptionとIRNV2の差が大きすぎたので、アンサンブルしても意味がありませんでした。
画像の前処理を変更したモデル同士でのアンサンブルや、Pseudo LabelingしたXception+しないIRNV2でのアンサンブル等も考えられます。

・ガウス過程でモデルの適正さ?を計算し、アンサンブルの重み付けを最適化できるらしい(未確認)

・輝度勾配ヒストグラムを局所特徴量として扱うことができる(普通画素のみだが)らしい(未確認)

注意すべきポイント

適切に評価できる評価体形を作らない限り、改善が実際に進んでいるか全然わからなくなります。

基本的に最新のモデルになればなるほど最終的な精度は高くなりやすい傾向にあります。
最新のモデルは下記で検索可能です。
State of The Art…Image Classification on CIFAR-100
レビュー論文と言って、論文をまとめた論文があるのでそちらで検索も非常に早いです。
下記は2017年時点のレビュー論文です。
Deep Convolutional Neural Networks for Image Classification: A Comprehensive Review

英語が苦手でも大丈夫。
ページごとGoogle翻訳するか、PDFはGoogleDriveに保存してDocumentで開くとテキスト化できます。

その他:画像水増し

下記のように画像に加工を加える方法があるので、それぞれ試します。
・ノイズを加える
・平滑化する(ガウシアンフィルタ)
・明度・彩度の調整
・グレースケール化
・ImageDataGeneratorの改良
→ とはいえ、CNNのフィルタ構造と同じような感じで調整すると意味がないので、その点は気を付けます。

1,200画像データから適切に学習するのは非常に難しいので、いろいろな角度から画像を学習できるようにします。

その他:画像水増し対応モデルの作成

上記に対応できるモデルを作成します。
グレースケール化であるとRGB3色から白黒1色になるため、一般的なモデルのファインチューニングは困難になる・・・のか?
(RGBからグレースケールへの計算式は存在するので変換は可能か?)

最後に

一位になりたかった
・・・いろいろと試せ、理解が進んだので良かったです。

やってみたではなく、何を考えたか、なぜそう考えたかを包括的に書いた記事が少ないので、少しずつですがアップしていこうと思います。
その他コードやデータサイエンス関連の質問はR:Twitterまでお願いします。

[Meisyo] ver 0.31 リリース情報

名将と呼ばれた者達をver 0.31にアップデートします。

更新概要

1・投手配球設定の追加
2・練習試合マッチング方法の修正
3・課金決済の導入
4・その他
5・アンケート

更新詳細

投手配球設定の追加

投手の配球パターンを1球ごとに設定できるように変更しました。

これまでは、変化球球種の比率しか選ぶことができず、投球コースについては完全自動(ランダム)でしかありませんでした。
そのため、基本的なコースの投げ分けや、左右打者での投げ分けを設定することができませんでした。

今回の追加で、コースと変化球球種の投げ分けおよび左右打者での投げ分けを設定することができるようになりました。

懸念点としては、投球パターンを数多く設定しないとワンパターンになるので、逆に打たれやすくなる可能性が高いことです。

開発中の図

ある程度直観的に使用することが可能です。
パターン数を多くする等の操作には慣れが必要…。

練習試合マッチング方法の修正

練習試合のマッチングが意図しない動作になっていたので修正を行いました。

詳細としては下記の通りです。
同レートの人が存在する順位にマッチングされた場合、特定の人に挑戦が固定される不具合がありました。
同順位を上からソートし、ソート順で相手が決まる(固定してしまう)問題がありました。
そのソートをランダム化して同順位では一人しか確認しないアルゴリズムにしました。
そうすることで固定した相手と当たらないようにしました。

ご報告ありがとうございました!

課金決済の導入

アイテムショップからPayPalを介して課金できるようになりました。
名将ポイント1,000P(有償)が1,100円です。
有償のみで特別に購入可能なものは、プレミアムアカウントチケット30日が500Pです。

詳細は購入場所に提示している利用規約をご覧ください。

その他

操作上分かり辛かったり、説明がない部分の追記を行いました。
UIの変更、機能の追加(公式戦優勝報酬の追加、公式戦の未実施試合自動消化等)についても行っております。

例
・連盟戦チーム振り分け時のメール一斉送信
・投手配球設定に関わる変更

アンケート

機能の追加についてのアンケートを行います。

最後に

追加練習用の新コンテンツ(熟練プレイヤー向け)を準備しています。

探索
→ 選手強化(選手体力使用)、アイテム獲得
 MineCraft的なもので、1マップ(自動生成有)でアイテムを制限時間内に探していくゲームになる予定です。

実装は2~3月になると思いますので、お楽しみに。

「実践!Chainerとロボットで学ぶディープラーニング」を作ってみた

実践!Chainerとロボットで学ぶディープラーニングを購入しました。
単純にロボを動かすのは楽しいです。
周りに意外と買った人が居ないようなので、レビューを書きました。

所感

ちょっとお高いけど、土日を使えば深層学習およびRaspberry Piの大枠を掴むことが可能

「71,800円+税」なので、個人で買うには結構お高いですが、
資料も的確にまとまっていて380Pなので(ちょっと間違い有り)すごく理解が深まります。

とりあえず箱が大きい

結構スペース取るなあ・・・

中身はわりと小さい

組むのに時間がかかる

意外と複雑なんだよなあ・・・

動くと楽しい(暗くてごめんなさい!)

こんな人におすすめ

・プログラミング言語を1度でも触ったことがある人
・深層学習に興味がある人
・まとまった時間が取れる人

EV3(ロボット)の組み立てにわりと時間がかかるので、興味がないと続かないような気がします。コードもそこまで難しくない(ちょっと間違いが有るので修正必須)ので、コードを書いたことがある人なら最後まで学ぶことが可能だと思います。

流れ

説明書には下記のとおりに書かれていますが、

所感では、以下の通りです。

  1. EV3の組み立て(50%)
  2. LEGOの説明書より配置を理解することが難しい。パーツは余る。

  3. RaspberryPiの環境構築(20%)
  4. 説明不足(「WLANのアドレスで接続しないと動かない」が書いていないなど)がたまにある。

  5. 教材を理解する(10~20%)
  6. わかりやすい。

  7. 実際に動かす(10~20%)
  8. 楽しい。

組み立て図は常に構造を理解していないと難しいので、少しずつ組み上げていくには骨が折れると思います。
そのため、土日で一気にやる方が良いかと。

わかること

・Pythonの記法
・深層学習の基本的な数学
・センサーとのやりとりのプログラミング
・Chainerの使い方

プラスアルファ
・遊び心
 EV3のストップ&ゴーがあまりにも激しいので、向き変更の際に少しずつ減速・加速するプログラムを独自で作りました。バックしながら動かすのとかも。
・時間はコスト
 学習器を学習させるのには普通のPCだとかなりかかりそうです。GPUを使える環境かだったのですが、待ち時間が結構ありました。

わからないこと

・深層学習の原理
・深層学習をもっと深く
その他はだいたい網羅されていると思います。

まとめ

「ちょっとお高いけど、よくまとまっている学習には便利なキットである」のは間違いないと思います。
この記事で使用している画像は実践!Chainerとロボットで学ぶディープラーニングに関係する企業様の画像を使用しています。

次に何をやろうか?

簡単にいろいろやりたい方
→ How Do I Get Started?

実践的なコードの書き方
→ 3 ways to create a Keras model with TensorFlow 2.0 (Sequential, Functional, and Model Subclassing)
英語嫌ならGoogle翻訳かMouse Dictionaryがおすすめです。

深層学習についての知見がほしい
→ サーベイ論文(深層学習の論文を網羅的に調べた論文)
PDFはDropboxに落として、HTML化してから読むといいと思います!

[Meisyo] ver.0.20(a)更新情報

Meisyoでは10月20日にver.0.20(a)として下記アップデートを行いました。かなり大がかりな変更も含まれているため、詳細に報告いたします、

安定し次第、0.20(b)に移行します。
安定するまではバグが出る可能性があります。バグ等はご連絡ください。
追記 大きなバグ等は発生していないため、11/1にアップデートを行った際に0.20(b)に移行します。

  1. 自動練習の追加(手動練習の廃止)
  2. バランス再調整(速球投手の地位向上)
  3. 練習試合のランキング変更方式の変更
  4. 各種追加

10/15まで行ったアンケートの結果も併せて公表しました。
多くのご意見のうち、可能な範囲で今回アップデートに反映しています。
今後のアップデートにも反映させる予定です。

自動練習の追加(手動練習の廃止)

手動で練習を行う方式から、行いたい練習を指示して自動で練習を行う方式に変更しました。指示は24時間有効で、練習途中での追加指示が可能です。

練習が指示されていない(または指示時間が切れた)場合は、メイン画面にて自動でポップアップ表示されます。練習状況の確認は、練習状況のNewsから「練習実施中…」のリストをクリックしてください。

1日あたりの経験値獲得量は現行とほぼ変わりません。行いたい練習を行っている選手には+20%の経験値補正がかかります。その他の選手には補正はかかりません。

1日あたりのアイテム獲得期待値が0.3(気合の塊のみ)から2(気合の塊、プロキュア30、モード5換算)へ増加しました。

加えて、アイテムが自動獲得になったためアイテム予備倉庫機能を追加しました。獲得アイテムがある場合のみ、練習指示状況の下に表示されます。(下記参照)
練習で獲得したアイテムは予備保管庫(10個、プレミアムアカウントチケット使用中で15個)に入れられ、保管庫からあふれたアイテムはすべて削除されます。そのため、獲得状況を頻繁に確認したほうがより多くのアイテムを獲得できるようになります。なお、予備倉庫が満タンになっていても経験値獲得には影響しません。

頑張れば少し差がつく程度ですが、バランスを考えて調整していきたいと思います。

バランス再調整(速球投手の地位向上)

速球投手が他の投手に比べ相対的に打たれやすかったので調整しました。
具体的には速球(基礎値+育成値+強化値)によって打者のスイングスピードがランダムで低下するようにしました。
※この調整は投手の能力に依存し、打者の能力には依存しません。
※速球投手以外の投手にはほとんど影響しません。

大きな変更としては、打者スイングスピードが低下したため被HR率が下がり、被打率(WHIP)等も低下します。

被HR率

防御率

WHIP

計算方式としては、スイングスピードが減少処理(100%→N%へ変化)されます。
Speed_S(SP=106): Mean…75.95%
Control(SP=32): Mean…98.41%
Speed_A(SP=100): Mean…80.64%

計算式は以下の通りです。

$G['bt_swsp_mn'] = (1.0373 ** $G['pc']['S_p_sp']) * mt_rand(0, 100) / 100; # 減少量
$G['bt_swsp'] *= (100-$G['bt_swsp_mn'])/100;

※高ければ高いほど打たれにくいように調整するため、指数関数を使用しています。

投球を1,000回実施すると以下のような分布になります。
mt_rand(等分布)を使用しているため、等分布します。

変更理由

「高校野球の華、速球投手が打たれやすくてどうするんだ・・・?」という思想の元変更。
全ての能力値にそれぞれ意味があるようにしたいと考えています。(物理計算をチューニングするのは難しい)

これでゲームバランスの大きな調整は終わった、と思いたい・・・。
なおバランス調整が終わった後、イベントとしてたまーに現在は木製バットですが金属バット化して超打高な月を作ったり、常に雨でボールが転がりにくい月を作る予定です。

練習試合のランキング変更方式の変更

練習試合レートが勝利時に蓄積される形式に変更されました。引き分け・負けても減りません。シーズン開始時にリセットされます。
なお、報酬形態も変更しました。シーズン1勝または+50ptごとに報酬が増加します。

変更理由

「頑張った人のチームが上のリーグに行って報酬を多く獲得してほしい。何もしない人は落ちるべき。」という思想の元変更。
今までは強いチームが上に行って、相対的に弱いチームが下に行っていた。
弱いチームの人は何回練習試合をしても、報酬をもらえなかったため。
1回でも勝てば報酬が増えるので、練習試合に挑戦する意味を持たせたかった。

もちろん強いチームは勝ちやすいので戦えば上位に行きやすい。
シナリオも経験値、報酬がおいしい(優勝でプレミアムアカウントチケット)のでどっちにするかーって話ですね。

各種追加

アンケートでのご意見を取り入れ、いろいろと追加しました。

アップデートに追加済

  1. 全高校野手・投手成績比較
  2. 便利(だが打たれにくい投手の傾向は発見できなかった…)

  3. 公式戦歴代優勝校、トーナメント表
  4. トーナメント表は結果のみ表記

  5. 能力値の説明
  6. 育成情報で公開。
    必要があれば計算式も表示します。

  7. スタメン変更方式の変更
  8. これまでは1人1人切り替える必要があったが、直観的に使用できるようにクリックのみ変更可能に。
    割と便利になった気がする。

  9. 成績指標の追加
  10. UZR等は1球ごとのログを取らないと難しそうなのでやめておく。(以前の試合でのエラー頻発の原因…コードを見直したらできそう)

  11. 修行の表示改良
  12. 修行前に修行後のLvとExp状況を見れるように変更。

  13. 選手の手動LvUP
  14. EXPがLvUP必要以上にある場合(リセット使用時、修行時等)、LvUPをカード詳細のLv[+]から可能に。

今後追加予定

     

  1. 連合戦
  2. だいたいの仕様:1か月1回、エントリーした監督たちを(だいたい同じくらいになるように)ランダムに配分した連合を作り、それで5日のリーグ戦を戦う。参加者、優勝者には賞品あり。毎月5-10日エントリー、11-15日チーム発表・16-20日連合戦21日結果発表。
    連合は3名(以下、連合メンバーと呼称)を基本にし、チームメンバーには各高校から各6人の選手をエントリーします(2名の場合は各9人)。各高校からのメンバー選出は当該高校の監督しかできませんが、連合チームの組替え・采配は連合メンバーなら誰でも可能とします。そのため、チャットでのコミュニケーションを取りながらチームの構成を考えると良いでしょう。これまでとは一味違ったMeisyoをお楽しみください。

  3. バント
  4. 高校野球では当たり前に使われているが、一番難易度が高そうなので待機中。
    物理計算にバントを入れて、成功率をどうチューニングするかが問題。

  5. 練習方法のバランス再調整
  6. 投球技術がバランス悪い(2種UPのみ)ので。
     

  7. スキルLv・スキルLvUPの実装
  8. たぶん同レアリティのスキルであればUP可能になる。気合の塊と同じ感じ。UP幅は+1~2%/Lv程度になる予定。
     

  9. マイナススキルの追加
  10. 選手にはそれぞれ苦手なことがある

  11. 挑戦相手の追加
  12. 実装済み:守備、先発、打撃(ミート型)
    中継ぎ(体力とは何かLvで長く使えない)、走塁+打撃(守備は死んだ)、パワー、肩(キャッチャー想定)の追加を予定。

アンケート結果

実施フォーム(終了済)

投票順(有効票14)
・練習(挑戦キャラクターの追加)3
・UI・UXの追加2
・采配機能の追加2
・各種記録閲覧可能に1
・ゲームバランス調整(上限解放)1
・ゲームバランス調整(不得意スキルの追加)1
・その他新要素等1
・その他1
・公式戦(表示追加)1
・自治会及び対抗戦の追加1

色々なご意見ありがとうございました。
データだけではどうにもならない部分が多いにあるので、参考にしていきます。
また、直接言っていただいた場合は可能であれば対応します。

Jupyter Notebook「7ブロック」でかんたん複数動画のフレームの切り出し(Movie To Image)

はじめに

機械学習のために、動画から画像のデータセットを取り出す必要がありました。
動画 → 画像の変換には多くのフリーソフトはありますが、
今回使おうとした「MOV」ファイルが読み込めるまともなフリーソフトがありませんでした。
※更新停止していたり、ウイルスだと判定されたり散々でした…。

というわけで、Pythonで書いたほうが早いなとなりました。

下記にコードを載せています。
よくわからない人はダウンロードにあるzipファイル(テスト動画付き)を使ってください。

Windows、Macどちらでも使用可能です。

ダウンロード

MovieToImage.zip

コード

使用するモジュールを読み込みます。

import os
import cv2
import glob
from tqdm import tqdm

※インストールできていなければインストールしてください。

基礎設定を行います。
デフォルトでは、Jupyterファイルのあるフォルダの中にimageフォルダを作り、その中に保存します。

file_name = 'IMG_0001.MOV' # 読み込みファイル
fps = 30 # fps(1秒に1枚切り取る)
dir_name = 'image' # 保存dir
image_name = '_img%s.jpg' # 出力ファイル名

保存用関数を設定します。
1フレーム単位で保存するとほぼ動きがない画像になるので、1秒単位で切り分けます。

def video_to_images(video_file=file_name, root_dir='', save_dir=dir_name, image_file=image_name, fps=fps):
    # saveディレクトリを作成
    if not os.path.exists(save_dir):
        os.makedirs(save_dir)
    
    # 各ムービーのディレクトリを作成
    image_dir = save_dir + '/' + video_file + '/'
    if not os.path.exists(image_dir):
        os.makedirs(image_dir)
        
    # 未設定の場合fpsを設定
    fps = 60 if fps is None else fps
    
    # ビデオファイル名を設定
    read_file = root_dir + video_file
    
    # Videoをフレーム単位で切り取る
    i = 0
    cap = cv2.VideoCapture(read_file)
    while(cap.isOpened()):
        flag, frame = cap.read()
        # 読み込めなかったら終了
        if flag == False:
            break
        # 1枚進める
        i += 1
        if i % fps != 0: # 1秒1枚を出力
            continue
        fname = image_dir + video_file + image_file % str(int(i/fps)).zfill(6)
        cv2.imwrite(fname, frame)
        print('No.%s Save [%s]' % (i, fname))

    cap.release() # capリリース

テストとして1動画ファイルを切り取ってみます。
エラーでなければ、imageフォルダの中に動画ファイル名ごとにフォルダが作成されています。
1枚ずつ切り出すことができました。

# 1画像ファイルを加工
video_to_images()

複数動画の加工を行います。
複数動画を入れるフォルダと拡張子名を設定します。

# 基礎設定
dir_mov = 'mov' # ムービー用dir
file_ex = '.mov' # ムービー拡張子名

globでファイル検索をかけ、filesに格納します。

files = glob.glob(dir_mov + "/*" + file_ex)

実行します。
簡単に処理が終わりました。

# Cut開始
for path in tqdm(files):
    print("#-----------------------------------------------------------#")
    print("# Start MovieToImage... file:%s" % path)
    print("#-----------------------------------------------------------#")
    file_name_one = path.split("\\")[-1]
    video_to_images(video_file=file_name_one, root_dir=dir_mov)

おわりに

Yahoo知恵袋か何かで、「動画ソフトでプリントスクリーン→ペイントすると良い」と当たり前のように書かれていて驚きました。
それちょっと辛くない?時間かかりすぎでしょ・・・というわけで今回作成しました。
何かあればメッセージを残してくださいませ。

[Python]グリッドサーチを軽量化し、チューニングしたパラメータも反映する機構を作る

パラメータチューニング方法であるグリッドサーチ、
確かに自動で実行してくれて、すごく便利なのですが問題点があります。

めっちゃ時間がかかる
もし、下記のパラメータ設定のモノを全てグリッドサーチしようとすれば、ゲーミングPCでも余裕で24時間を超えます。
・・・特に時間制限のあるColaboratoryでは安易に使えない。

ただ、パラメータ1つ1つグリッドサーチすると
それはそれで「そのパラメータでしかモデルを捉えられない」という問題をはらみます。

そこで、今回は下記の方法の実装を行いました。

グリッドサーチしてパラメータ1を「A」に決める
↓
パラメータ1は「A」として、
グリッドサーチしてパラメータ2を「B」に決める
↓
パラメータ1は「A」、パラメータ2は「B」として、
グリッドサーチしてパラメータ3を「C」に決める
↓

なぜこんなことをしたいのかというと、
適当にパラメータを投入すれば勝手に上手くやってくれるし、時間もかからない機能が欲しかっただけです。

モジュールのインポート

モジュールを必要最低限呼び出します。
scalerはお好きなものをご使用ください。

from sklearn.pipeline import Pipeline
from sklearn.model_selection import GridSearchCV
from sklearn.preprocessing import MinMaxScaler, StandardScaler, RobustScaler, Normalizer
scaler = MinMaxScaler()
#scaler = StandardScaler()
#scaler = RobustScaler()
#scaler = Normalizer(copy=True, norm='l2')

学習器のインポート

今回は回帰の問題だったのでSVRや回帰型XGBを使用しましたが、分類も対応可能です。

from sklearn.svm import SVR # サポートベクター回帰
import xgboost as xgb # XGB

データのインポート

pandasで読み込まれており、前処理は終わっているX、yを仮定します。
ただし、正規化(または標準化等)はまだ実施していないものとします。

X = ...
y = ...

パラメータ設定

設定できる項目はまだまだありますが、一つの例として記載します。

cv_list = {
    'XGB':
    {
        'discriminator': xgb.XGBRegressor(objective='reg:linear'),
        'random_state': [0],
        'booster': ['gbtree', 'gblinear'],
        'n_estimators': [1, 3, 5, 10, 20, 30, 50, 100, 200],
        'max_depth': [3, 4, 5, 6, 7, 8, 9, 10],
        'subsample': 1.1 - 10 ** np.linspace(-1, -0.8, 20), # ~1
        'learning_rate': 10 ** np.linspace(-1.2, -0.9, 20),
        'gamma': np.linspace(0, 1.0, 20), # 0~
        'reg_lambda': 1.1 - 10 ** np.linspace(-1, -0.3, 20), # ~1
        'reg_alpha': -0.1 + 10 ** np.linspace(-1, -0.3, 20) # 0~
    },
    'SVR':
    {
        'discriminator': SVR(),
        'kernel': ['poly', 'rbf', 'sigmoid', 'linear'],
        'degree': np.arange(1, 10, 1),
        'C': 10 ** np.linspace(-5, 3, 5),
        'gamma': 10 ** np.linspace(-5, 0, 5),
        'epsilon': 10 ** np.linspace(-5, 0, 5)
    }
}

あるパラメータが必要だと思えば追加すれば良いと思います。

グリッドサーチ実行

複数個のモデルのグリッドサーチに対応しています。
パイプラインを使ってデータ分割→scaler実施→model.fit→判定を自動で行います。
パイプラインを使用すると、param_gridをモデル名__パラメータ名にしなければならないことにも対応しています。
最後に一番良いパラメータを表示します。

for model in cv_list:
    # model作成
    print("#---------------------------------------------------------#")
    print("#", model, "loaded")
    print("#---------------------------------------------------------#")
    model_test = cv_list[model]['discriminator']
    model_name = "model"
    pipe_model = Pipeline([("scaler", scaler), (model_name, model_test)])
    model_param = cv_list[model].copy()
    del model_param['discriminator']
    
    # paramごとにCV
    best_param = {}
    for param in model_param:
        # make
        param_one = model_name + "__" + param
        param_grid = {}
        param_grid[param_one] = model_param[param]
        param_grid.update(best_param) # 以前のチューニング結果を反映
        print(">> Tuning '%s' is..." % (param))
        model_cv = GridSearchCV(pipe_model, param_grid=param_grid, cv=3,
                                return_train_score=False, n_jobs=-1, verbose=0)
        model_cv.fit(X, y)
        # Best
        best_param[param_one] = [getattr(model_cv.best_estimator_.steps[1][1], param)]
        print('Best Params:', best_param[param_one])
    
    # last
    print(">> All Best Params is...")
    print(best_param)

GridSearchCVに自作評価関数を入れることも可能です。

scoring=make_scorer(func_scoring, greater_is_better=True)

最後に

10分もかからずにグリッドサーチが終わると思います。
すべてのパラメータを総当りで行うのはコスト(時間)がかかりすぎるのでおすすめはしません。

お役に立てたのであれば嬉しい限りです。