鹿児島ハードチル同好会

情報学部の大学生です。深層学習(Tensorflow)とかUbuntuとか音楽とかガジェットに興味があります。バンドもしてたりする

TensorflowのEmbedding Visualizationでカッコよく可視化したい

TensorBoardがカッコいい

大学でディープラーニングを用いた研究をやろうと意気込んでおります。 TensorBoardでカッコよく可視化してみたいと思ったところ、アイドル顔認識の先駆者である偉大なすぎゃーんさんのこのツイートを発見

memo.sugyan.com

ご丁寧に記事まで書いてくださっており、動かそうと思ったのですがTensorflowのバージョンが1.0系に変わってからの大幅な仕様変更でいろいろと問題があったので躓いたところを簡単にまとめました。

実行環境

学習済みモデルとデモのダウンロード

Tensorflowのページを参考に学習済みのモデルをダウンロード

Image Recognition  |  TensorFlow

ターミナルを開いて

 $ git clone https://github.com/tensorflow/models

にてクローリングします。

学習済みのモデルのディレクトリが tensorflow.models.image.imagenet から models/tutorials/image/imagenet へと変更されていました

同様にすぎゃーん氏のデモもクローリングします

 $ git clone https://github.com/sugyan/tf-embedding-visualization-demo/blob/master/README.md

デモディレクトリ内にmodels/tutorials/image/imagenetにある classify_image.pyをコピーしておきます

以下修正

このまますぐ実行できれば良いのですがそうなるとこの記事の存在意義がなくなってしまいます。 以下修正箇所です。 コメントアウトが修正前、その下が修正後のコードです

# main.py

# line 5
# from tensorflow.models.image.imagenet import classify_image
import classify_image


# line 35
# embedding_var = tf.Variable(tf.pack([tf.squeeze(x) for x in outputs], axis=0), trainable=False, name='pool3')
embedding_var = tf.Variable(tf.stack([tf.squeeze(x) for x in outputs], axis=0), trainable=False, name='pool3')


# line 40
# summary_writer = tf.train.SummaryWriter(os.path.join(basedir, 'logdir'))
summary_writer = tf.summary.FileWriter(os.path.join(basedir, 'logdir'))

# line 53 ~ 55
# for i in range(size):
#     rows.append(tf.concat(1, images[i*size:(i+1)*size]))
# jpeg = tf.image.encode_jpeg(tf.concat(0, rows))

for i in range(size):
    rows.append(tf.concat(images[i*size:(i+1)*size], 1))
jpeg = tf.image.encode_jpeg(tf.concat(rows, 0))

恐らくこれで動くかと思われます。 Tensorflowが1.0系になってからとそれ以前の情報で溢れているインターネットでこれからも頑張りたいという気持ちのもと、うどんを眺めています

参考ページ

TensorFlow の "AttributeError: 'module' object has no attribute 'xxxx'" エラーでつまづいてしまう人のための移行ガイド - Qiita

TensorFlow v1.1 / 移行 > tf.pack()はtf.stack()になった - Qiita

TypeError: Expected int32, got <prettytensor.pretty_tensor_class.Layer > of type 'Layer' instead. · Issue #48 · google/prettytensor · GitHub

【Twitter API】西野カナっぽい歌詞を自動生成するbotをつくった【形態素解析】

形態素解析西野カナbot

f:id:fuchami:20170216063410p:plain



大学で自然言語処理に関する講義を受講しました。
最終課題としてTwitter APIを用いて自動ツイートをするbotの作成を課されたので、自動で西野カナっぽい歌詞を生成するbotを作成。

データを収集する

とりあえず、西野カナの歌詞を大量に収集しなければいけません。
Twitter APIを用いるということで、歌詞もTwitter上から集めることに。
西野カナ歌詞botなるものがいくつも存在するためその中からこちらのアカウントを使用させて頂きました。

西野カナ 歌詞bot (@kanayan_lyrics) | Twitter
西野カナ歌詞bot (@kana_lyrics) | Twitter


データ収集コード

実際にツイートから歌詞を収集していきます。1回のツイート取得数は200ツイートまでなのですが、オプションパラメータであるmax_idを指定することによってそれより前のツイートを繰り返し取得していきます。

#-*- encoding:utf-8 -*-
import requests_oauthlib
import requests
import json
import sys, os

#ツイートの取得
def get_tw(account):
    session = requests_oauthlib.OAuth1Session(
        "XXXXXXXXXXXXXXXXXXX", #Consumer Key
        "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", #Consumer Secret
        "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", #Access Token
        "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" #Access Token Secret    
    )
    url = 'https://api.twitter.com/1.1/statuses/user_timeline.json'
    res = session.get(url, params = {'screen_name': account, 'count':200,'include_rts':False})
    maxid = 0
    i = 0
    f = open("data.txt", "a")
    #ツイートを取得
    while True:
        res_text = json.loads(res.text)
        for r in res_text:
            if maxid > r['id'] or maxid == 0:
                maxid = r['id']
            tw = r['text'].encode('utf-8')
            f.write(tw)
            i = i +1
        if  500<= i:
            break
        res =  session.get(url, params = {'screen_name': account, 'count':200,'include_rts':False, 'max_id': r['id']-1})
    #APIの呼び出し・ステータスコード判定
    if res.status_code != 200:
        #正常終了しなければエラー表示
        print ("Twitter API Error: %d" % res.status_code)
        sys.exit(1)
    f.close()
    return 0
#main関数
def main():
    #ツイートを取得する
    get_tw("kanayan_lyrics")
    get_tw("kana_lyrics")
    print("ツイートを収集しました")
    return 0
if __name__ == "__main__":
    sys.exit(main())

収集結果

こんな感じで収集できました。だいぶ重複しているとは思いますが、文字数にして104020文字、5304行西野カナの歌詞を取得することができました。
なかなかカオスです。

f:id:fuchami:20170216055850p:plain



歌詞を生成する

分かち書き

歌詞の生成の前に、日本語を分かち書きする必要があります。
英語は単語ごとにスペースが区切られてますが日本語はそんなことがないため単語ごと(正確には形態素ごと)に分ける必要があります。
MeCabっていう形態素解析エンジンがあるのでこれを使用。

歌詞生成 マルコフ連鎖 (N-gramモデル)

さて、いよいよ歌詞を生成します。
今回はマルコフ連鎖を用いて歌詞を生成していきます。
マルコフ連鎖に関してはニコニコ大百科が非常に分かりやすいので一読することをおすすめします。
マルコフ連鎖とは (マルコフレンサとは) [単語記事] - ニコニコ大百科

今回は連鎖数3で作成。あまり増やしすぎるとオリジナルの歌詞そのままを生成してしまう(ような気がした)ので。

歌詞生成コード

#-*- encoding:utf-8 -*-

from requests_oauthlib import OAuth1Session
import requests
import json
import sys, os, re
import MeCab
import random

#Mecabによってわかちがきを行う
def wakati(text):
    #余計な文字列を除去
    text=re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", text)
    text=re.sub('RT', "", text)
    text=re.sub('#kana_nishino', "", text)
    text=re.sub('/', "", text  )
    text=re.sub('#西野カナ', "", text)
    t = MeCab.Tagger("-Owakati")
    m = t.parse(text)
    result = m.rstrip(" \n").split(" ")
    return result

#連鎖数3のマルコフ連鎖にて文章生成
def create_tw(wordlist):
    markov = {}
    w1 = ""
    w2 = ""
    w3 = ""
    endword = ["。", "!", "?"]
    for word in wordlist:
        if w1 and w2 and w3:
            if(w1, w2, w3) not in markov:
                markov[(w1, w2, w3)] = []
            markov[(w1, w2, w3)].append(word)
        w1, w2, w3 = w2, w3, word
        
    count = 0
    sentence = ""
    w1, w2, w3 = random.choice(markov.keys())
    while count < len(wordlist):
        tmp = random.choice(markov[w1, w2, w3])
        #句読点などの区切りがついたら文章作成を終了
        if tmp in endword:
            break
        sentence += tmp
        w1, w2, w3 = w2, w3, tmp
        count += 1
        if count > 20:
            break
    return sentence

#main関数
def main():
    CK = 'XXXXXXXXXXXXXXXXXXXXXXXXX'                             
    CS = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'         
    AT = 'XXXXXXXXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' 
    AS = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
   
    filename = "data.txt"
    src = open(filename, "r").read()
    # ツイート投稿用のURL
    url = "https://api.twitter.com/1.1/statuses/update.json"
    #わかち書き
    wordlist = wakati(src)
    #文の作成
    tw = create_tw(wordlist)
    # ツイート本文
    params = {"status": tw}
    # OAuth認証で POST method で投稿
    twitter = OAuth1Session(CK, CS, AT, AS)
    req = twitter.post(url, params = params)
    # レスポンスを確認
    if req.status_code == 200:
        print ("posted tweet:" + tw)
    else:
        print ("Error: %d" % req.status_code)    
    return 0

if __name__ == "__main__":
    sys.exit(main())
    

生成結果

上手く出来たものをいくつか紹介。
なかなか西野カナの雰囲気のが出てると思います。




失敗例

英単語が何故か単語ごとにスペースがなく見づらくなってしまいました。
いろいろやってみものの結局挫折。
pytnoで日本語と英語を扱うのはなかなかキツいっす。

【学割】Apple Musicに加入してる学生は早めにしたほうがいい

Apple Musicはいいぞ


ここ最近Google Play MusicからApple Musicに乗り換えたんですけど聴ける曲が多すぎでDigにヤバみが出てます。追いつかないですね。


そんなApple Musicは月額980円なのですが、学生向けの学割プランがありまして半額の月額480円で利用できます。
学生のありがたみが分かる。


f:id:fuchami:20170214052310p:plain


やり方


簡単です。すぐ出来ました。
Appleの公式ページに詳しく書いてますがまあみんな読むのだるいよね。

support.apple.com


既にApple Musicに加入していて、iPhoneで変更する場合

  1. musicのアプリを開く
  2. 下のバーの"For You"をタップ
  3. 右上のアイコンをタップ

f:id:fuchami:20170214052130p:plain


  1. 下の方に"登録"があるのでタップ

f:id:fuchami:20170214052248p:plain


  1. 利用登録欄に"学生(1ヶ月) 480円"があると思うのでタップ
  2. UNiDAYSから学生であるかどうかの確認をさせるのでがんばって証明してください

証明方法 (大学のメールアドレスを使わない方法)

学校から配布されるメール(@なんちゃら.ac.jpみたいなやつ)の入力がいるみたいなんですけどぶっちゃ使ってます?僕は使ってないです。なんだったかも覚えてないです。
じゃあどうすんのかって、こうします


学校のポータルにサインインします。一部の国では、学校の Wi-Fi ネットワークに接続されている状態で Apple Music のサービスにアクセスするか、学校用のメールアドレスを提示する必要があります (Apple Music の学生メンバーシップの提供状況については、こちらの記事でご確認ください)。サインイン後、学生としての在学が証明されたら、「Open this page in 'Music'? (このページをミュージック App で開きますか?)」というメッセージが表示されます。「Open (開く)」をタップしてミュージック App に戻り、登録手続きを最後まで進めてください。

引用元:Apple Music の学生メンバーシップを利用する - Apple サポート




ということで、大学のWi-Fiに接続しながらだったら別に自分の普段使いのメアドでもいけちゃいます。(僕はいけました。)
ポチポチやってて、ほいほいとその日には認証できました。
(学割プランが実際に適用されるのは翌月からです。)



このあたりは大学によりけりだったりすると思うのでそのときは大人しく大学のメールアドレスで認証を行って下さい。