FloydHubのトライアルで使えるGPU利用枠が激減..

なんと、

FloydHubのトライアルで使えるGPU時間が10/1から大幅に減ってしまったようです... ><

100時間 → 2時間 (◞‸◟)

Our promotional period, during which we offered 100 hours free GPU has already ended. If you signed up during that period, you should still have the credits. The old plans are valid till Oct 1st 2017

Is my free plan changed to 2 hours free GPU according to new free plan? - FloydHub Forum

まだ残り時間あったよな? と、

新しいGPUジョブを動かしてみると、

Error: You do not have enough credits to run this job. Please upgrade your plan or buy a powerup to continue running jobs

無情なエラー(T-T)

100時間のうち80時間ぐらいは残っていたはずなのに0になっていました..。

公式ページによると今のFreeプランは

  • first 2 GPU hours (トライアル特典)
  • 400 GB storage
  • 20 CPU hours / mo.

のようです。

CPUしか動かせないんだったら 自分のMac でいいのではないでしょうか。。

ちなみに、有料プラン は

  • 最初の10時間は 約154円/時間(基本)
  • それ以降は 約77円/時間 (追加分)

のようです。

RNNで感情分析(Sentiment Analysis)

RNN(Recurrent Neural Network)を使って感情分析(Sentiment Analysis)をしてみます。 今回はchainerを使って実装していく中で理解に苦労した点を図にまとめます。

RNNとは何なのか? RNNの基本、については以下の記事が分かりやすいです。

ちなみに、3つ目の記事は おなじみのMNIST文字認識が CNN ではなくRNN で実装されていて興味深かったです。

Sentiment Analysis

感情分析を理解および実装していく上でポイントになるのは以下の2点です。

  1. CNN(画像分類)との違い
  2. ロスの計算

CNN(画像分類)との違い

CNNで画像分類をするときは次のようにバッチサイズ分のデータを1回で入力します。

f:id:sanshonoki:20170929051417p:plain:w450

一方、感情認識では 時間方向の依存関係、つまり文脈を学習するので時間方向に入力していく必要があります。

f:id:sanshonoki:20171002202054p:plain

つまり、バッチサイズ分の文章を取り出し、それを位置ごとに切って文章の長さの回数分ネットワークに入力します。

ちなみに、このように時間方向に展開していくことを unroll や unfold と言うようです。

ロスの計算

感情分析の場合、文章(単語列)に対して1つのラベルがつくので最後の単語の出力(y_n)に対してのみ教師ラベルとのロスを計算します。それ以外の出力は無視します。

f:id:sanshonoki:20171002215028p:plain:w400

chainer のコードだと

if j <  seqlen - 1:
    model.predictor(x)
else:
    accum_loss = model(x, t)
    accum_loss.backward()

あるいは

loss = model(x, t)
if j == seqlen - 1:
    accum_loss += loss 
    accum_loss.backward()

といった形になります。

また、TensorFlowの場合は時間方向にまとめて展開できる dynamic_rnnモジュールを使って以下のように表現できます。 (Embed、LSTM層とFC層が別々になっています。)

f:id:sanshonoki:20171002215824p:plain:w400

感情分析のチートシート的なまとめ図

記憶の定着のためにまとめてみました。( •ᴗ•)

f:id:sanshonoki:20171002215211p:plain

その他のRNN応用でのロスの計算

感情分析以外もRNNの応用はいくつかあり

  • 文章生成
  • 機械翻訳(seq2seq)
  • 画像のキャプショニング

などがあります。

f:id:sanshonoki:20171003210723p:plain:w400

(画像は http://karpathy.github.io/2015/05/21/rnn-effectiveness/ から)

入力と教師ラベルの与え方でよく混乱するのでこれも図にしました。

ポイントは

  • 推論(予測)フェーズと違って出力はロスの計算以外に使わない(推論フェーズでは出力が次の入力になる)
  • 入力(x)を1つずらして教師ラベル(t)を作る

かなと思います。

文章生成(character-level language model)

f:id:sanshonoki:20171002215425p:plain:w350

機械翻訳(seq2seq)

f:id:sanshonoki:20171003211707p:plain

<GO><EOS> は文章の始まりと終わりを表す記号です。

機械翻訳では入力単語列を反転して入れると結果が良いらしいです。 順番に入れると1番目の単語を予測する上で大事な最初の入力単語の情報がデコーダの時点でより失われるのだと勝手に理解しています。

chainerでの実装

データセット

UdacityのDeepLearning nanodegree講座のSentiment-RNNの課題で使ったデータセットを使います。

映画レビューの英語テキストを positive, negative の2クラスに分類するという課題で実装が正しければ数epochの学習の後、おおよそ80%程度の精度が出るはずです。

コード

chainerの昔のptbのサンプルを参考にしつつ実装してみました。

(trainerを使っても実装しようとしましたがなんかうまく動いてくれません。。><)

github.com

動かしたところ80%の精度が出たのでアルゴリズムは大丈夫のようです。ƪ(•◡•ƪ)"

次はこれを使って Slackのメッセージを分類してみようと思います。

画像認識APIと翻訳APIを使って日本語キャプショニング

Microsoft Computer Vision APIのキャプショニングの性能がイケてるらしい。

日本語キャプションには未対応なので Microsoft Translator API と組み合わせて実験してみました。

f:id:sanshonoki:20170906051557j:plain

Serverless Frameworkを使って画像認識と翻訳のAPIを叩くLambdaファンクションを作成し、それをAPI Gatewayでhttpエンドポイントとして公開。 botからはそのhttpエンドポイントを叩いて結果を投稿します。

いくつか画像を試してみる !!

Webからいくつか画像を拾って試してみます。

競馬編

http://www.geocities.jp/sunday_silence1977_2/photo/06arima/arima06 http://www.geocities.jp/sunday_silence1977_2/photo/06arima/arima06

認識結果「大勢の人々の前に立っている人々の集まり」

確かに、その通り。


http://worldsports-c.com/images/page_img/850072bef46c88f6760738608d72dca6.jpg http://worldsports-c.com/images/page_img/850072bef46c88f6760738608d72dca6.jpg |

認識結果「馬の背に乗っている人々のグループ」

正解 :-)


http://sp.jra.jp/beginner/yosou/img/a-4-3/01.png http://sp.jra.jp/beginner/yosou/img/a-4-3/01.png

認識結果「馬に乗っている男」

惜しい。

アニメ編

続いて子どもがハマっていた仮面ライダーエグゼイド。

http://www.toei.co.jp/tv/ex-aid/story/__icsFiles/afieldfile/2017/03/17/1_1.jpg

認識結果「雪の道にスキーに乗っている人々のグループ」

そうか、ひと昔ふた昔前はスキーウェアも派手だったもんな.. と納得。


http://www.toei.co.jp/release/movie/__icsFiles/afieldfile/2017/08/23/artimgpreview.jpg

認識結果「柵の側にスケートボードに乗っている若い少年」

影がスケートボードか?!

ドラマ編

現在放映中で毎週楽しみにしている月9ドラマ、コードブルーから

http://blogs.c.yimg.jp/res/blog-8f-c7/yukki_na0716/folder/769139/92/9384592/img_4?1266915772 http://blogs.c.yimg.jp/res/blog-8f-c7/yukki_na0716/folder/769139/92/9384592/img_4?1266915772

認識結果「窓の前のテーブルに座っている人々のグループ」

主役の二人もグループと言われてしまうと。。 ╮(•ω•)╭


https://cdn.mdpr.jp/photo/images/2c/099/w700c-ez_220ea4338737fc71234305b81c9a56655ce703ebcd4f7d10.jpg https://cdn.mdpr.jp/photo/images/2c/099/w700c-ez_220ea4338737fc71234305b81c9a56655ce703ebcd4f7d10.jpg

認識結果「部屋に立っている人々のグループ」

確かにその通りです。


https://instagram.com/p/BXt1ua5hGBJ/media/?size=l https://instagram.com/p/BXt1ua5hGBJ/media/?size=l

認識結果「カメラのポーズをとる比嘉真奈美ら」

有名人は名前も認識するようですね。嗚呼、その他になってしまった浅利くん… w

キャプショニングの使いみちについて考えてみる

実験してみての感想はエンジニア目線では機械学習でここまで認識できるのか!という驚きです。 一方でユーザー目線ではこのシュールな文章は一体何に使えるんだろう?とも

そんな中、

面白いかも?? と考えついたのは、

自分を客観視するツール

です。ƪ(•◡•ƪ)"

例えば、感情が高ぶって怒っているとき「中年のオジサンが怒って話している」と機械に客観的に言われれば 冷静になれるのではないでしょうか。。

コード

今回、使ったコードはこちらです。 github.com

APIキー

から取得できます。(無料プランあり)

Microsoft Computer Vision APIMicrosoftアカウント が必要で Microsoft Translator API のほうは Azureアカウント が必要です。

無料の範囲は

となっていてちょっとした実験レベルは無料で十分まかなえます。

MicrosoftアカウントでのMicrosoft Computer Vision API の試用は30日間で有効期限が切れてしまいますが Azureアカウントで CognitiveServiceAPIのインスタンスを立てれば継続して使えるようです。(1分あたり20回のレートリミットの制限もなかったです)

実装で参考にしたページ

Python3.6で sls invoke local すると `Error: spawn python3.6 ENOENT`

サーバレスなアプリケーションを構築するためのツール Serverless Framework で Python3系が使えるようになったということを最近知って

dev.classmethod.jp

の記事を参考に使ってみました。

ただ、

sls invoke local とローカルでファンクションを実行すると、

Error: spawn python3.6 ENOENT

が出ちゃう.. ╰(゚x゚​)╯

デプロイした remote で実行すると問題なく動くのだが気持ち悪い…

Webを検索すると同じエラーが出ている人がいたのでこれを参考にやってみる

https://doruby.jp/users/nakamatsu/entries/%60sls-invoke-local%60-%E3%81%99%E3%82%8B%E3%81%A8%60Error–spawn-python3-6-ENOENT%60%E3%81%A8%E8%A8%80%E3%82%8F%E3%82%8C%E3%81%9F

が、これでもうまくいかない… (◞‸◟)

解決方法

結局うまくいった方法はこれでした。

$ brew install python3   # pyenvで3.6をインストールしてもNG。system環境に3.6をインストールする
$ pyenv local system  # パッケージをインストールするときはsystem環境のpythonにして行う
$ pip3 install requests  # pyenvのsystem環境で必要なパッケージ(この場合は requests)をインストール

この3ステップでエラーが出なくなりました。(2番目のステップが抜けてました)

スッキリです。(•̀ᴗ•́)و

ちなみに、パッケージがすでにインストールされていてファンクションを実行するだけのときは pyenv で system環境 にしなくても実行できるようです。

ステッドラーのシャープペンシル

4歳の息子がひらがなの練習をしているとき、たまたま 普通の鉛筆ではなくステッドラーシャープペンシル を使うことがあった。

そしたら、普通の鉛筆だとグー握りしてしまうのにこのシャープペンシルだと自然とちゃんと握れて、またすごく書きやすそうではありませんか ( ☉་☉ )

ステッドラー シャープペンシル 1.3mm 771 https://www.amazon.co.jp/dp/B001OUU32A

1.3mmの太芯シャープペンシル

【特長】・1.3mmと太い芯のシャープペンシルの為、軽い筆圧で筆記することができ安定感があります。

・エルゴノミックコンセプトに基づく三角軸と直径16mmの太軸、 スベリ止め加工の施されたグリップゾーンが特徴です。 ノック部には繰り出し式の字消しを装着しております。

もちろん、自分では書きやすいと思って 1000円 を投じ使っていたわけですが、小さい子どもがサクサク文字を書き出したのを目の当たりにし その価値は十分あったなと実感しました ( •ᴗ•)

同時に、子どもは嘘をつけない正直なユーザー であることを改めて実感

CordovaによるReact.jsアプリのネイティブアプリ化でハマったところ

React.jsで作ったスライドショーアプリをCordovaでネイティブアプリ化しました。ƪ(•◡•ƪ)

f:id:sanshonoki:20170815204337p:plain:w200

github.com

いくつかハマった点があったのでまとめておきます。

なお、アプリ化の手順に関しては以下のサイトが大変参考になりました。♪(・ω・)ノ

qiita.com

Cordovaの導入やコマンドの使い方に関してはこのあたり

ハマった点… ><

react-routerを使ったときに起動ページのパスが違う

対策

各環境ごとに起動ページのパスが違うのでルーティングを複数用意する必要があります。

起動ページのパス
React.jsアプリ /
cordova serve ios /ios/www/index.html
cordova emulate ios /Users/xxx/Library/Developer/CoreSimulator/De…25B7D/CordovaReactSlickExample.app/www/index.html

なので、react-routerを使うときは

<Router>
    <Route exact path="/" component={App} /> <!-- Reactのwebアプリでの確認用 -->
    <Route path="*/index.html" component={App} /> <!-- cordovaアプリ用 -->
</Router>

のように2種類の初期ページ表示用のルーティングをもっておく必要がありました。

public/以下のassetsへのアクセス

これに関連して、React.jsアプリのpublic以下のassetsへの参照も少し工夫する必要がありました。

  1. 初期ページを起動時パス取得用のダミーページ(Homeコンポーネント)にする

     <Router>
         <Route exact path="/" component={Home} />
         <Route path="*/index.html" component={Home} />
     </Router>
    
  2. ダミーページで起動時のパスを取得し、Globalな state に保存する

     class Home extends React.Component {
         constructor(props) {
             super(props)
         }
    
         componentDidMount() {
             // 起動時のパスを取得する
             const root = path.dirname(this.props.location.pathname)
    
             // stateに保存するアクションを呼ぶ
             this.props.setRootPath(root)
         }
    
         render() {
             // 実際の初期ページへリダイレクトする
             return (
                 <Redirect to="/login" />
             )
         }
     }
    
     const mapDispatchToProps = dispatch => {
         return {
             setRootPath: (path) => dispatch(setRoot(path)) // 起動時パスを保存するアクション
         }
     }
     export default connect(null, mapDispatchToProps)(Home)
    
  3. assetsにアクセスするときは起動時パスを考慮した形で行う

    const root = getState().home.root // 起動時のパスをstateから取得
    const url = `${root === '/' ? '' : root}/images/slick/pictures.json`
    fetch(url)
    

Fetch API cannot load file:///android_asset/www/xx/xxx.json. URL scheme “file” is not supported のエラーが出た(Android

対策

fetchは file:// をサポートしてないらしく XMLHttpRequest を使って記述します。

https://github.com/github/fetch/pull/92#issuecomment-140665932

function fetchLocal(url) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest
    xhr.onload = function() {
      resolve(new Response(xhr.responseText, {status: xhr.status}))
    }
    xhr.onerror = function() {
      reject(new TypeError('Local request failed'))
    }
    xhr.open('GET', url)
    xhr.send(null)
  })
}

fetchをfetchLocalに置き換えればok。

このエラーはiOSのときには出ませんがfetchLocal置き換えの副作用はありません。

Unable to post message to https://www.youtube.com. Recipient has origin file://.のエラーが出た

対策

config.xml に以下を追加

<allow-navigation href="https://*youtube.com/*" />

https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-whitelist/

モバイルでのYouTube埋め込み動画の自動再生

先日、React.jsの練習Webアプリ(スライドショー)でBGM機能としてYouTube埋め込み動画の自動再生を実装しました。

自動再生は以下のように埋め込みURLにautoplay=1のパラメータをつけることで実現できます。

<iframe width="854" height="480" src="https://www.youtube.com/embed/B2fPYlGKdXM?autoplay=1" frameborder="0" allowfullscreen></iframe>

が、モバイル端末では自動再生されないことが判明… 。

どうやら仕様のようです。(´Д`。)

YouTube Player API Reference for iframe Embeds  |  YouTube IFrame Player API  |  Google Developers

ChromeSafari などのモバイル ブラウザでは、HTML5 <video> 要素を再生するには、ユーザーの操作(プレーヤーをタップするなど)による起動が必要です。以下は、Apple のドキュメントの抜粋です。

「警告: ユーザーが費用負担する携帯電話ネットワーク経由で要求していないダウンロードを防止するために、iOSSafari では組み込みメディアを自動再生できません。必ずユーザーが自分で再生します。」

この制限があるため、autoplay、playVideo()、loadVideoById() などの関数およびパラメータはすべてのモバイル環境では動作しません。

結論

結論から書きましょう。

  • 音声をミュートした状態ではモバイル端末でも自動再生は可能
  • 音声再生ありだとモバイル端末では自動再生はできない

ということになります。

モバイル端末での埋め込み動画の自動再生

この記事に書いてあるように無音の状態であれば自動再生は可能です。

qiita.com

ただし、player.unMute()した時点で残念ながら自動再生はストップしてしまいます。

再生ボタンをそれとなく表示する

ならばユーザーに再生ボタンを押してもらうしかありません。

最初にトライしたのは以下の記事に書いてあるiframe埋め込み動画にオーバーレイして再生ボタンを表示させる方法です。

Youtube Iframe API not working for mobile devices? - Stack Overflow

  <style>
    iframe#player {
      position: absolute;
      left: 0x;
      top: 0x;
      width: 200px;
      height: 100px;
    }

    #play_button {
      position: absolute;
      left: 0px;
      top: 0px;
      opacity 0;
      width: 200px;
      height: 100px;
      pointer-events: none;
    }
  </style>

のように自前の再生ボタンをiframe埋め込み動画と同じ位置に同じサイズでオーバーレイします。 pointer-events: none;によって再生ボタン上でのクリックイベントはiframe埋め込み動画の要素上で受け取れ、動画を再生できます。

オーバーレイした状態 オーバーレイしない状態(参考)
f:id:sanshonoki:20170814051612p:plain:w100 f:id:sanshonoki:20170814051622p:plain:w100

クリックした場所によって挙動が違う..?!

自前の再生ボタンのクリックした場所によって挙動が違うという現象に出くわしました..。

  • 想定通りにインラインで動画が再生され、オーディオも流れる
  • なぜか新しいタブを開いてフルスクリーンで動画が再生される

調べてみると、

f:id:sanshonoki:20170814054505j:plain

曲名の部分が https://www.youtube.com/watch?v=... へのリンクとなっており、ここをクリックすると新しいタブで開いてしまいました。

ユーザーがボタンのどのエリアをクリックするかは分かりません。。

なので、このままではNGです。

showinfo=0を指定すると曲名のリンクは非表示にできますが代わりにYouTubeロゴのブランディングが表示されてこれがhttps://www.youtube.com/watch?v=...へのリンクになって同じことになります。。

f:id:sanshonoki:20170814085851j:plain

Playerのパラメータを以下のようにチューニングするとPC閲覧時にはYouTubeロゴを非表示にできましたがモバイル閲覧ではYouTubeログは表示されてしまいます..。(>д<;)

playerVars: {
    showinfo: 0,
    controls: 2,
    modestbranding: 0,
    playsinline: 1,
},

最終的に行き着いた方法

YouTubeの再生ボタンをそのまま利用することにしました。

具体的には表示サイズをYouTubeの埋め込み動画の再生ボタンを同じ大きさにすることで再生ボタンのエリアのみが露出するようにしました。

const opts = {
  width: '40', // same size of play button
  height: '30', // same size of play button
  playerVars: {
    showinfo: 0,
    controls: 2,
    modestbranding: 0,
    playsinline: 1,
  },
}

この設定にすることでモバイル端末での表示は以下のようになります。

f:id:sanshonoki:20170814090934j:plain

この状態だとボタンのどこをクリックしても新しいタブで再生することなくinlineで動画(音声)が再生されます。:-)

ただ、iOSiPhoneの場合は動画再生は全画面表示となるらしくこの方法でもインラインでのBGM再生はできませんでした.. (iOS10からはインライン再生できるようです)