読者です 読者をやめる 読者になる 読者になる

TeraBytesMemo

プログラミング関連で自分のメモをまとめて書いたり気が向いたら書いたり

ニコ動のごちうさ1話のコメントでword2vecで遊んでみた

ふと、ごちうさ1話のコメントから、いわゆる"ごちうさ難民"の人工知能を作れないかと思い行動。

今回は、趣旨が違って、拾ったコーパスをword2vecに通したらどうなるかのお遊びだけ。

コーパス

ニコニコ動画のコメントはドワンゴより以下のようなデータが提供されています。

情報学研究データリポジトリ ニコニコデータセット

が、ごちうさ1話のコメントを取るには古すぎたので、自分でスクレイピングしました。

その際に普通にスクレイピングしても取れなさそうなので、Seleniumを使いました。

スクレイピングの方法としては

  1. ログインが必要になるので、自分のアカウントでログイン
  2. ごちうさ1話の動画へ移動
  3. コメントが読み込まれるまで待機
  4. コメントが読み込まれたらスクロールしながらコメントのDOM情報をMongoDBに保存

という手順をSeleniumで組みました。このクローラーを1日に1回のペースで回し、約35000件のコメントを保存しました。

なおコメントに加えて、コメントの投稿時間や、コメントの投稿した再生時間も保存してあります。

ソースコードは以下に用意しました。READMEとかまだできていないですが参考にしていただければと。

githubの src/gochi_usa_scrape.py が実行スクリプトとなっています。(環境変数にユーザー名のパスワードの設定必要あり)

github.com

スクレイピングに関して

今回行った成果は、以下を参考にしたうえで行いました。
robots.txtの内容も確認いたしました。
当成果に対して何か問題があればご連絡お願いします。

Webスクレイピングの注意事項一覧 - Qiita
Webスクレイピングの法律周りの話をしよう! - Qiita
niconico利用規約 - niconico

分かち書き

こういうデータを使う際には決まって文を単語に分ける分かち書きという作業が必要があります。

その際には、形態素解析mecabと、すげえmecabの辞書mecab-ipadic-neologdを用いました。

この辞書、アニメの単語とか本当に理解してすごいです。

mecabのインストール

OS X なのでbrewを使いました

brew install mecab
brew install mecab-ipadic

また、python3で処理をしたので以下のようにライブラリをインストール

pip install mecab-python3

mecab-ipadic-neologd

以下の記事を参考にしてインストール

kivantium.hateblo.jp

mecabはインストール済みなので

git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git
cd mecab-ipadic-neologd
./bin/install-mecab-ipadic-neologd

python上でmecab-ipadic-neologdを使う場合は

mt = MeCab.Tagger('-Ochasen -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd')

と、タガーを定義する際に引数に辞書を指定する必要があります。

また、今回のコーパスは複数行にわたるAAが顕著に見られました(しかも各行が分けられて撮ってきている)

よって、形態素解析及び分かち書きの際は、文中の"記号"が6割以上超えた場合にコーパスから排除するという処理を行いました。

コードしてはこんな感じ。sent_parser関数が文の分かち書きで、parser関数が複数の文をまとめて分かち書きできる。

#!/usr/bin/env python
# coding: utf-8

import MeCab


def surface_iter(node):
    #「文頭」を無視
    node = node.next

    while node.next:
        yield (node.surface, node.feature)

        node = node.next

def sent_parser(sentence):

    dic = '/usr/local/lib/mecab/dic/mecab-ipadic-neologd'

    config = '-Ochasen'
    config += " -d {}".format(dic)
    mt = MeCab.Tagger(config)
    mt.parse('')

    node = mt.parseToNode(sentence)
    parsed = surface_iter(node)

    try:
        parsed = list(parsed)
        surfaces, features = zip(*parsed)
        new_surfaces = [sur for sur, fea in parsed]

        n_kigou = len([x for x in features if '記号' in x])
        score = n_kigou / len(features)

        return new_surfaces if score < 0.6 else []
    except ValueError: # zipに失敗したものは排除。
        return []


def parser(docs):
    parsed = map(sent_parser, docs)
    parsed = [x for x in parsed if x]

    return parsed

word2vec

gensimのword2vecを使って類似度計算を行いました。

word2vecによって、単語をベクトル表現することができ、コサイン類似度で単語の類似度を調べることができます。

"word2vec"についてわからない人は、ググると出てくるので是非ググってください。有名なモデルです。

類似度計算の際は、上記のコードの関数で分かち書きした単語列の頭に投稿した再生時間を示す記号を投入。

(例えば、再生時間01:00に投稿されたものはTM_01という記号を入れる)

具体的に、コードにするとこんな感じ(x['time'][:2]がコメントの投稿した再生時間(分単位)、x['parsed']は分かち書きしたコメント)

[['TM_' + x['time'][:2]] + x['parsed'] for x in result]

こういう細工をすることで、各再生時間で使われる単語を見ることができてニヤニヤできますw

ちなみに、各コメントを3つにコピーしてかさ増ししました。

word2vecにかけて、類似度計算をしてみた結果。

川の類似度。表示結果は川と類似する単語です。川に対して詳しい。

f:id:TeraBytesMemory:20160704174817p:plain

EUの類似度。類似する単語がめっちゃタイムリー。

f:id:TeraBytesMemory:20160704175148p:plain

濃度の類似度。空気と関係していたり薄くなったり不穏。

f:id:TeraBytesMemory:20160704180838p:plain

TM_16のデータをかさ増ししていないモデルの結果で。この時間は弾幕が濃い。

f:id:TeraBytesMemory:20160704181413p:plain

同じくかさ増しせずにTM_22。線路へほっぴんジャンプ♪

f:id:TeraBytesMemory:20160704181936p:plain

TM_○○の類似度はかさ増ししない方が面白い結果が出ました。

cordovgaのチュートリアル

cordovaのチュートリアル(下記)をやってみましたが、うまくいかなかった点が幾つかあったのでメモ。

Apache Cordova Tutorial

インストール

npm -g cordova

プロジェクト作成

cordova create workshop com.yourname.workshop Workshop

cordova createに続く引数はそれぞれ、プロジェクトのディレクトリ、プロジェクトの識別子、プロジェクト名です。

プラットフォーム設定

cd workshop
cordova platforms add ios
cordova platforms add android

プラグインの設定

そのまま入力してもうまくいかなかったので、ググったらURLから直接とってくる必要があるとのこと。

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-device.git
cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-console.git

iOSのエミュレート

OSXの場合は、Xcodeが必要となります。インストールし忘れて手詰まった。

インストールした後も、起動して初期設定しないとうまくいかないので注意。

cordova emulate ios

Androidのエミュレート

Android SDKが必要となります。私は、Android SDKAndroid Studioからダウンロードしました。

Android Studioをインストールして起動。その後、PreferenceでAndroidSDKの項へ移動してSDKパスを確認。

下記の画像でハイライトされている項です。

f:id:TeraBytesMemory:20160629094342p:plain

確認したパスは、以下のように~/.bash_profileに追加。

### Android SDK
export ANDROID_HOME=/path/to/Android/sdk

その後エミュレートのコマンド

cordova emulate android

を叩いても

Hint: Open the SDK manager by running: /Users/yterazawa/Library/Android/sdk/tools/android
You will require:
1. "SDK Platform" for android-23
2. "Android SDK Platform-tools (latest)
3. "Android SDK Build-tools" (latest)

とエラーが出るので、指示通り処理を実行。また、一覧にandroid-23がなかった場合。設定の

Appearance & Behavior > System Settings > Android SDK

よりインストール。私の勘違いで、一覧にあるのかもしれない。

しかし、上記の処理をしてもエラー。

Error: No emulator images (avds) found.
1. Download desired System Image by running: /Users/yterazawa/Library/Android/sdk/tools/android sdk
2. Create an AVD by running: /Users/yterazawa/Library/Android/sdk/tools/android avd
HINT: For a faster emulator, use an Intel System Image and install the HAXM device driver

また上記の作業を実行したらエミュレータが起動しました。

その他

後は、チュートリアルのページの通りに手順を踏んでうまくいきました。

話が変わりますが、cordova上で、react-routerが使えない。下記を参考にしてもうまくいかなかったので誰か教えてくれると幸いです。

github.com

pythonのWebアプリをデプロイするためのIaaS入門に必要になった知識

さくらインターネットさんのさくらのクラウド2万円無料券があったのと、line botを作りたくなったり、elasticsearchが使いたくなったのでさくらのクラウドを使用。

そこで、IaaSを使うのに、Unixサーバの知識とかいろいろ必要で苦労したのでやったことをまとめ。とりあえずAWS使ったほうが楽そうだった...

ちなみに、今回はUbuntu16.04を使いました。

ここで説明する知識は

  • nginx(サーバー)
  • systemd
  • gunicorn(python用サーバーアプリケーション)
  • freenom(無料DNS
  • Let's encrypt(無料SSL証明書作成)

です。最小限の公開ならばSSL証明書は必要ないですが、line botの作成に必要になったので記載。ページの信頼性の向上もできますし、覚えて損はないかと。

(2016/6/19追記:経験したトラブルへの対策も書きました)

サーバー設立

nginxを使用。

sudo apt install nginx

インストールしたサーバーのIPにアドレスに接続すれば、Welcome to nginx!のページが表示されるはず。

サーバーの設定には、/etc/nginx/nginx.confという設定ファイルや/etc/nginx/conf.d/以下、/etc/nginx/site-enable/以下にある設定ファイルを編集・作成で。

ちなみに、nginxを使った時に面倒だったのがnginxの再起動や停止など。頻繁に使いました。エイリアス書くと楽かも。

sudo nginx # 起動
sudo nginx -s stop # 停止
sudo nginx -s reload # 再起動

systemdの使い方

サーバーなどの自動起動に使いました。

よく使うコマンドや実際にsystemdを使った例を記載します。

よく使ったコマンド
sudo systemctl restart # systemctlの再起動
elasticsearchの自動起動

ubuntu16.04でもaptでインストールしたelasticsearchに専用の設定ファイルも用意されているので

systemctl enable elasticsearch.service

で稼働可能に。詳しい適用の仕方は以下のURLにて。

15分で作る、Logstash+Elasticsearchによるログ収集・解析環境 - さくらのナレッジ

サーバーアプリ公開(python

pythonだとgunicornを使うのがクッソ楽だった。ちなみに今回使ったpythonのバージョンはは2.7.11。

gunicornのインストールはpipで。ちなみにvirtualenvは使っていません。

sudo pip install gunicorn

例えば、bottleなんかでサーバーを作る際は下記のようなコードを書きます。

#!/usr/bin/env python
# coding: utf-8

from bottle import request, route, default_app

@route('/')
def hello():
    return 'Hello world!'

app = default_app()

このコードを /var/www/app.py として保存した場合、

systemdでgunicornを自動起動させるスクリプトは下記のようになりますね。

#!/bin/bash

PID=/var/run/gunicorn.pid

if [ -f $PID ]; then rm $PID; fi

cd /var/www/app.py
exec gunicorn app:app --pid=${PID}

runapp.shという名前で保存したスクリプトをsystemdで自動起動をさせるスクリプトを書く

[Unit]
Description=gunicorn
After=syslog.target

[Service]
ExecStart=/path/to/runapp.sh
Restart=always
KillSignal=SIGQUIT
Type=notify
StandardError=syslog
NotifyAccess=all

[Install]
WantedBy=multi-user.target

設定ファイルの書き方はどこかから参考にしたのですが、どこか忘れた・・・参考記事さんごめんなさい。

無料DNS

サーバードメインが決まらなかったので、freenomという無料のDNSサービスを利用。こちらもお試しに使いたかったので。

Freenom.com

ドメインの発行には、IPアドレスが必要となります。

さくらのクラウドのサーバ一覧より、発行してもらうサーバの詳細画面に移動し、NIC項へ移動、そこでIPアドレスを確認することができます。

登録には数分かかります。しばらくしたら、発行したドメインより、サーバのホームページに移動できるはずです。

SSL証明書

試したかっただけなので、無料のSSL証明書を発行。Let's encryptを使いました。

ちなみに、Line bot を制作するのに、Let's encryptは使えないとのことでしたが、2016年6月時点ではLet's encrpytの証明書でもbotが作れました。

SSL証明書の取得にはホスト名が必要なので、freenomなどであらかじめ取っておきましょう。

インストール

git clone https://github.com/certbot/certbot.git

証明書作成コマンド。事前にnginxやapacheなどサーバーを止めないと実行できません。作成の際に規約の同意が聞かれます。

./certbot-auto certonly --standalone --email admin@example.com -d example.jp -d www.example.jp

証明書の作成が完了すると、メッセージが表示されます。

そのメッセージ内の'Your certificate and chain have been saved at' の後に書いてあるパスが証明書の保存場所ですね。

んで、nginxの設定

server {
    listen       443 ssl;

    ssl_certificate /etc/letsencrypt/live/example.jp/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.jp/privkey.pem;
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDHE+RSAGCM:ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:!aNULL!eNull:!EXPORT:!DES:!3DES:!MD5:!DSS;

    server_name www.example.jp;

    access_log /var/log/nginx/app_access.log;

    location / {
        proxy_pass http://127.0.0.1:8000; # gunicornで立ち上げたサーバーのポート
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

記憶が曖昧なので、下記の参考記事読んだ方がいいかも...

参考

Let's Encrypt の使い方 - Let's Encrypt 総合ポータル

Let's Encryptの証明書をnginxに設定してhttps化した | tsuchikazu blog

トラブル

急にサーバーに接続できなくなった

以下のスクリプトを作成して実行。

ファイヤウォールのルールをリセットとあるが本当にこんなことをしていいのだろうか?

#!/bin/sh
echo "Flushing iptables rules..."
sleep 1
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT


insanelabs.net

その他

やりたいこと

dockerを使った開発環境やデプロイをやってみたい

ssl通信について勉強してまとめたい

Arduinoをシミュレータで入門してみた

IoTには興味あるけど、実際に機器を買うまでモチベーションが至らなかったので、シミュレータから入った話。

ラズパイとWebの力で何かしたい、でも実際に何ができるかわからないから、名古屋版IoT縛りの勉強会! IoTLT vol1 - connpass に参加して聴講。

どうやら電子工作でのWebはArduino + esp8266の方が簡単らしい、ということでArduinoから入ることにしました。

123d circuits

Bring ideas to life with free online Arduino simulator and PCB apps | 123D Circuits by Autodesk

入門するにあたって使ったシミュレータです。

123d circuitsは、ウェブ上で動くシミュレータで、作った回路やコードを公開できます。

どうやら、パーツを壊すこともできるみたいで、「抵抗間違えてArduinoやパーツがお釈迦になった」なんていうハプニングも未然に防げるとか。

使ってみた感想、というか簡単な考察はこんな感じ。

メリット
  • プロトタイピングにはうってつけ。パーツや本体を買わなくても、シミュレータ上で試せる。シミュレータ上で失敗ができるので、本番で失敗しない。
  • 他人のソースコードや回路を観れたり、コピーして編集できる。他人の回路から勉強することも簡単にできる。
  • 回路を作ったら、必要なパーツが販促される。これ自体は良いことかは人によるが、これによって回路がすぐに実現できるかと。
  • なんとesp8266が使える。後述する設定で、Wifi通信ができるので、お試しでWeb通信も実装可能
デメリット
  • Arduinoの基本的なライブラリしか読めない。外部ライブラリを使いたければそのソースコードをコピペしろとのこと。
  • 動作が妙に遅い。シミュレータないの一秒が一秒以上であることがが普通。
  • esp8266でアクセスして、取得した結果がやけにおかしい。思った通りの情報を受け取れない。
  • pythonで書けない(憤怒)。巷ではpythonArduinoが書けるというらしいが、シミュレータにそんなものはサポートされているわけなかった。

以上のように、気軽に電子工作ができて共有もできるうえに、Wifi通信もシミュレートできる一方、

さすがになんでもかんでも作れるわけではない。そこについては、まあ仕方ないと割り切りましたが、さすがにWebから取得したレスポンスのメッセージが変ってのは腑に落ちませんね・・・

しかし、シミュレータ上で回路が落ちていたり、それを編集できるので、とりあえずArduino本体やパーツを買って、作りたい回路を見つけたorできたら早速作るのもアリですね。

で、シミュレータで遊んで作ったものを以下に記載します

制作物

ArduinoでWebAPIにアクセスして、取得した結果をLCDに表示させてみました。

回線だけは完全に丸パクリ。配線はその上で必要最低限分勉強しただけで、コードを変えてみたって感じです。

とりあえずHTTPリクエストの結果まんま(?)LCD上に表示させました。

動かしてみたらわかりますが、JSONのデータらしいものが表示されます。どうも結果の挙動がおかしいです。

そのせいで、肝心な欲しいデータだけを抽出しようにも全くできない状態。

本当は、クライアント端末からLEDやモーターを操作したかったのに、謎の取得結果のせいで実現できない状態に。

今後は、原因究明をするか、諦めて本番で作るかですかねー・・・

esp8266への接続

esp8266での接続では、ssidを "Simulator Wifi" で、パスワードは未入力("")で接続ができます。

つまり、123d circuitsでWifiに接続するまでなら、こんな感じに書ける。

(他人のコードのコピペですが・・・)

String ssid     = "Simulator Wifi";	// SSID to connect to
String password = ""; // Our virtual wifi has no password (so dont do your banking stuff on this network)

void setup() {
  // Start our ESP8266 Serial Communication
  Serial.begin(115200);		// Serial connection over USB to computer
  Serial.println("AT");		// Serial connection on Tx / Rx port to ESP8266
  delay(10);				// Wait a little for the ESP to respond
    
  // Connect to 123D Circuits Simulator Wifi
  Serial.println("AT+CWJAP=\"" + ssid + "\",\"" + password + "\"");
  delay(10);				// Wait a little for the ESP to respond
}
参考にしました

qiita.com

denshibu.blog.fc2.com

その他

123d circuitsでdweet.ioという、簡単にWebAPIを作れるものを使いたかったです。(使ったけど)

これは、認証も登録もなしで簡単にWebAPIが作れるというもので、プロトタイプには超うってつけです。

URLやパラメータで簡単にデザインできます。

dweet.io

FlaskでRESTfulAPIを作ってみた

pythonでRESTfulAPIを簡単に書けないかなと思ったら、flaskでもできそうということで試してみた。

Flask-RESTful — Flask-RESTful 0.2.1 documentation

javascriptからajaxを使うにあたって、クロスドメイン制約が邪魔になる時もあると思いますが、CORSの設定もできるみたいです。

例えば、monaca(以下参照)を使ってアプリを作る時とかに使えそうですね。

ja.monaca.io

というわけで、CORSを適用したRESTfulAPIをつくってみました。

#!/usr/bin/env python
# coding: utf-8

from flask import Flask, request, jsonify
from flask_restful import Resource, Api
from flask_restful.utils import cors

# 初期設定
app = Flask(__name__)
api = Api(app)

class Count(Resource):
    cache = 0

    # GET時の挙動の設定
    @cors.crossdomain(origin='*')
    def get(self):
        Count.cache = Count.cache + 1
        send_msg = jsonify(num=Count.cache)

        return send_msg


api.add_resource(Count, '/count/')

if __name__ == '__main__':
    app.run()

挙動としては count/ にアクセスするとバッファのカウンタを1足して返す挙動をとります。

で、APIにアクセスする方をjsfiddleで書いてみました。

jsfiddle.net

挙動がおかしいですが、基本カチカチボタンを押すとカウンターに値が加算されます。

anaconda環境でflaskで開発したサーバーをherokuに上げる

pythonのリハビリとpython3覚えるために。

というかanacondaとherokuにはまだ慣れていないので要点だけメモ

割りと参考記事パクってつまんだだけです

1.仮想環境構築
conda create -n flask-in-heroku flask gunicorn
2.Procfileの用意

サーバーファイル名が main.py の場合

cat 'web: gunicorn main:app --log-file -' > Procfile
3.モジュールリストの用意
conda list -e > conda-requirements.txt
4.heroku上でアプリの作成
heroku create --buildpack https://github.com/kennethreitz/conda-buildpack.git

これでプッシュしてサーバーが動いていればok

参考:


github.com
qiita.com

存在していないDOM要素を検知する方法

JavaScript

EventListenerのAPIやMutationObserverについて調べても、

存在しないDOMについての監視する方法がなかったので、自分で書いてみました。

動的にDOMを生成するときに、便利でしょう。

実際、動的にDOMを生成する必要のあるケースがあって、そのDOMの生成の完了を監視する必要がありましたので。

以下にjsfiddleのURLを記載します。

Edit fiddle - JSFiddle

ここでは、特定クラスを監視するコードとなります。

正確には、fooの生成を監視するコードとなっていますが、存在していないfooクラスにも対応できています。

もし、fooクラスが生成が1つで十分でしたら、

fooクラス発見時に、MutationObserverを停止したりすればよいです。

実際のコード

一応実際のコードも貼り付けておきます。

var observeTo = "foo";

var documentObserver = function() {
    var option = { childList: true, subtree: true };

    var observer = new MutationObserver(findClassListener);
    observer.observe(document.body, option);

    return observer;
};

var findClassListener = function(mutation) {

    mutation.forEach(function(m) {
        var nodes = m.addedNodes;
        for(i=0; i<nodes.length; i++) {
            if (nodes[i].classList.contains("foo")) {
                console.log("A " + observeTo + " class DOM is generated !");
            }
        }
    });
};

documentObserver();
var rendered = document.createElement("div");
rendered.className += ' ' + "foo";
document.body.appendChild(rendered);