stMind

about Tech, Computer vision and Machine learning

openposeのjsonを解析する

昨日は、ナチョがウェストブロム戦で見せた奇跡のクリアをopenposeで関節推定してみました。openposeは、関節位置をファイル出力するオプションを用意しているので、jsonで出力して黒背景に関節位置を描画しましたが、その時の解析手順を簡単にまとめておきます。

jsonファイル出力

openpose実行時にwrite_keypoint_jsonを指定します。

$ ./build/examples/openpose/openpose.bin --image_dir ~/data/nacho --write_keypoint_json ~/data/results

ファイルの中身はこんな感じ。pose_keypointsが関節位置で、people以下に検出した人数分が出力されます。

{
        "version":1.0,
        "people":[
        {
            "pose_keypoints":[
                247.668,514.465,0.89905,247.702,527.27,0.87725,236.549,527.306,0.878132,233.307,541.693,0.918291,236.415,544.831,0.709778,258.863,527.235,0.795694,263.66,540.037,0.772329,269.998,554.436,0.744358,246.054,556.101,0.807442,246.055,575.247,0.484067,0,0,0,258.864,556.007,0.797664,260.464,573.657,0.435004,266.807,583.267,0.163514,246.067,511.293,0.856283,249.24,509.715,0.886759,241.254,514.496,0.671889,250.916,514.453,0.700287
            ],
            "face_keypoints":[
                
            ],
            "hand_left_keypoints":[
                
            ],
            "hand_right_keypoints":[
                
            ]
        }
    ]
}

pose_keypointsは(x座標, y座標, 信頼度) x 関節数18で、54個の値が含まれています。それから、関節位置の順番ですが、 openpose/poseParameters.hpp at master · CMU-Perceptual-Computing-Lab/openpose · GitHubに定義された通り(POSE_COCO_BODY_PARTS)になっていて、backgroundは関節位置には含まれませんので注意。さらに、関節位置の結合順序は、POSE_COCO_PAIRSに定義されてます。親切。

pythonjsonをパース

pythonは3.6.1です。シンプルにやると、

  • jsonをload
  • 検出された人数分のループ
    • 関節の全ペアのループ
    • ペアをcv2.lineで描画

たまに関節位置が求められなくて信頼度0.0になっているところだけ注意する必要があります。

import json
import numpy as np
import cv2

# jsonのロード
with open('nacho_0_keypoints.json', 'r') as f:
    data = json.load(f)

# 黒背景画像の用意
img_ = cv2.imread('original.jpg')
img = np.zeros_like(img_)

# 関節毎の描画色
colors = [(255.,     0.,    85.), (255.,     0.,     0.), (255.,    85.,     0.), (255.,   170.,     0.), (255.,   255.,     0.), (170.,   255.,     0.), (85.,   255.,     0.), (0.,   255.,     0.), (0.,   255.,    85.), (0.,   255.,   170.), (0.,   255.,   255.), (0.,   170.,   255.), (0.,    85.,   255.), (0.,     0.,   255.), (255.,     0.,   170.), (170.,     0.,   255.), (255.,     0.,   255.), (85.,     0.,   255.)]

# 検出された全員について
for d in data['people']:
     kpt = np.array(d['pose_keypoints']).reshape((18, 3))
     # 関節位置の全ペアについて
     for p in pairs:
         pt1 = tuple(list(map(int, kpt[p[0], 0:2])))
         c1 = kpt[p[0], 2]
         pt2 = tuple(list(map(int, kpt[p[1], 0:2])))
         c2 = kpt[p[1], 2]
         # 信頼度0.0の関節は無視
         if c1 == 0.0 or c2 == 0.0:
             continue
         # 関節の描画
         color = tuple(list(map(int, colors[p[0]])))
         img = cv2.line(img, pt1, pt2, color, 7)

cv2.imshow('nacho', img)

参照

以上のことは、下記を参照しています。

github.com

NLP for arsenal.com is difficult

スポナビライブの画面を開いたら、バギーズ戦の配信開始まで「1日と...」って明日かよー。

www.arsenal.com

最近というか、今年に入ってarsenal.comが変わった。試合毎に記事がまとまって見れるようになって、例えば上の vs Chelseaの記事は11本分がスクロールして見れるようになっている。スマホ対応ってことなんかな。

https://www.ostmodern.co.uk/projects/arsenal/arsenal-com/www.ostmodern.co.uk

2013年に以前のサイトから大規模なre-designがあって、その年と次の年はBest Website Awardをもらったとか。初めて知りましたよ。Ostmodernってデザイン会社が担当したみたい。 デザインは良くなったかもしれないけど、スクレイピングしてゴニョゴニョとかやりにくくなったような... 監督の発言だけを取り出したいんだけど、ある記事では使えるけど、別の記事では同じやり方は使えないとか。

明日の試合が終わったら、またちょっと試してみるかな。

Learning by Associationの論文を軽く読む

[1706.00909] Learning by Association - A versatile semi-supervised training method for neural networks

f:id:satojkovic:20170729175925p:plain (画像は論文より引用)

CVPR2017で発表された論文の一つ。ミュンヘン工科大学Googleの共著。 図にあるように、ラベルあり(A)とラベルなし(B)の画像が含まれるデータセットからCNNを学習する半教師あり学習のフレームワークの提案。

Associationというアイデアを導入。ラベルありの画像のembeddingと、ラベル無しの画像のembeddingを使って、図の赤矢印に示すように遷移後、異なるクラスになるとロスが発生する。

  • Walker loss
    • embeddingのdot productをsoftmaxとったものを遷移確率と考えて、A→B→Aとround tripした時の確率が以下の一様確率分布になるようにするロス
    • あるクラスのサンプルが、遷移して戻ってきた時に同じクラスのサンプルである確率は、1 /(Aの中でそのクラスのサンプルの総数)で、それ以外のクラスの場合は0
  • Visit loss
    • 全てのラベル無しサンプルに遷移するようにするロス
    • 1 / |B| の一様確率分布
  • Classification loss
    • 分類結果に対するロス
    • Walker lossとVisit lossだけでも収束する

MNISTとSVHN (Street View House Number)を使って評価。SVHNでラベルありサンプル500個のときにstate of the art。

CNNによるテキスト分類で学習済みword2vec(fastText)を使う

CNNとテキスト分類で検索すると、一番最初に出てくる[WildMLのチュートリアル]。

チュートリアルではembedding layerも含めて学習するようになっていますが、embeddingのところはFacebookが公開しているfastTextの学習済みword2vecで置き換えてやってみました。

github.com

変えたところは大きく3つの部分。

1. vocabとword_vecをfastTextからロード

英語の学習済みword2vecは6GBあって、gensimを使ってロードするのですが、毎回ロードしてると時間がかかってしまうので(20分近くかかる)、事前にvocabularyとword vectorsを別々のファイルに保存しておいて、そのファイルから読み込むようにしました。 後は、オリジナルと同じようにvocabulary indexの作成と、入力文中の単語をindexに変換しました。

print('Load pre-trained word vectors')
with open('fasttext_vocab_en.dat', 'rb') as fr:
    vocab = pickle.load(fr)
embedding = np.load('fasttext_embedding_en.npy')

pretrain = vocab_processor.fit(vocab.keys())
x = np.array(list(vocab_processor.transform(x_text)))

2. Embedding layerはfastTextをfeed

オリジナルでは、ランダムな値で初期化したWをembedding_lookupのparamとして、feedされる入力テキストの単語をベクトルにしていくのですが、Wは0で初期化して、placeholderとして受け取ったfastTextのword vectorsを代入して単語ベクトルに置き換えていきました。

W_ = tf.Variable(
    tf.constant(0.0, shape=[vocab_size, embedding_size]),
                trainable=False,
                name='W')
self.embedding_placeholder = tf.placeholder(
    tf.float32, [vocab_size, embedding_size],
    name='pre_trained')
W = tf.assign(W_, self.embedding_placeholder)

3. 学習

最後は、embeddingをfeed_dictで与えて学習をしました。

feed_dict = {
    cnn.input_x: x_batch,
    cnn.input_y: y_batch,
    cnn.dropout_keep_prob: FLAGS.dropout_keep_prob,
    cnn.embedding_placeholder: embedding
}

ロスとAccはこんな感じで進みました。

# The logs around step 1000 are as follows.
...
2017-07-08T13:12:27.329179: step 990, loss 0.178512, acc 0.953125
2017-07-08T13:12:28.902815: step 991, loss 0.133091, acc 0.984375
2017-07-08T13:12:30.473521: step 992, loss 0.148561, acc 0.984375
2017-07-08T13:12:32.041047: step 993, loss 0.21213, acc 0.90625
2017-07-08T13:12:33.617257: step 994, loss 0.230192, acc 0.9375
2017-07-08T13:12:35.223648: step 995, loss 0.222954, acc 0.9375
2017-07-08T13:12:36.822623: step 996, loss 0.161116, acc 0.96875
2017-07-08T13:12:38.437168: step 997, loss 0.224385, acc 0.921875
2017-07-08T13:12:40.073519: step 998, loss 0.258734, acc 0.921875
2017-07-08T13:12:41.649018: step 999, loss 0.207504, acc 0.953125
2017-07-08T13:12:43.215527: step 1000, loss 0.211571, acc 0.921875

Evaluation:
2017-07-08T13:12:44.823491: step 1000, loss 0.647888, acc 0.681

学習のロスは十分下がっていますが、Testのロスはそれに比べるとあまり下がっていきませんでした。過学習している様子なので、チューニングが必要そうです。

まとめ

CNNでテキスト分類するチュートリアルを、fastTextでやってみました。まとめてしまえばそれほど多くの変更はなかったのですが、いろいろ調べるのに1週間強はかかっていると思います。何かを身につけるのに簡単な道はないですね。

Try word2vec in 5 minutes

Task

word2vecを使ったテキスト分類の問題(質問文?回答文?)をやってみます。

Steps

  1. datasetの入手
  2. pre-trained modelの入手
  3. datasetとpre-trained modelのロード
  4. 学習
  5. 評価

1. datasetの入手

まずはデータセットが必要ですが、UCSD(カリフォルニア大学サンディエゴ校)の研究グループが、WWW2016で公開したAmazon question/answer dataを使います。

Amazon question/answer data

jsonの中身は、質問文、回答文の他に質問のタイプ(yes/noかopen-endedか)などが含まれていますが、ここでは質問文(questin)、回答文(answer)を使っていきます。

{'questionType': 'yes/no', 'asin': 'B00004U9JP', 'answerTime': 'Jun 27, 2014', 'unixTime': 1403852400, 'question': 'I have a 9 year old Badger 1 that needs replacing, will this Badger 1 install just like the original one?', 'answerType': '?', 'answer': 'I replaced my old one with this without a hitch.'}

2. pre-trained modelの入手

Google Newsのデータセットで学習されたmodelを使います。300万語の300次元ベクトルが含まれたmodelになっていて、3.4GBほどありますのでダウンロードには少し時間がかかります。下のページに、GoogleNews-vectors-negative300.bin.gzのリンクがあります。

https://code.google.com/archive/p/word2vec/

3. datasetとpre-trained modelのロード

pre-trained modelのロードはgensimを使えば簡単です。

# load pre-trained word2vec model
googlenews_w2v = gensim.models.KeyedVectors.load_word2vec_format('GoogleNews-vectors-negative300.bin', binary=True)

datasetの方はjson.loadを使いたい所ですが、シングルクォーテーションが使われていて、JSONDecodeErrorになってしまいます。仕方がないので、ast.literal_evalを使います。

# load datasets
# you cannot use json.load because this json file is invalid format...(using single quote)

questions = []
answers = []
with open('qa_Appliances.json', 'r') as f:
    for line in f:
        js = ast.literal_eval(line)
        questions.append(js['question'])
        answers.append(js['answer'])

参考。 stackoverflow.com

4. 学習

次は、学習です。 最初に、データセットを学習用とテスト用に分割します。

# Split dataset into train and test set
qa_texts = np.array(questions + answers)
qa_labels = np.zeros(len(qa_texts), dtype=np.int)
qa_labels[len(questions):] = 1  # question: 0, answer: 1

qa_idx = np.random.permutation(len(qa_texts))
qa_texts = qa_texts[qa_idx]
qa_labels = qa_labels[qa_idx]

X_train, X_test, y_train, y_test = train_test_split(qa_texts, qa_labels)

特徴量ベクトルの抽出は、シンプルに分類したいテキストに含まれる全単語のベクトルの平均を使うことにします。このアイデアは、下記のブログを参考にしました。また、識別器はRandomForestを使います。

nadbordrozd.github.io

# Simple word embedding feature by averaging word vectors for all words in a text
# ref: http://nadbordrozd.github.io/blog/2016/05/20/text-classification-with-word2vec/#comment-3233012354
class MeanEmbeddingVectorizer(object):
    def __init__(self, word2vec, dim):
        self.word2vec = word2vec
        self.dim = dim
        
    def fit(self, X, y):
        return self
    
    def transform(self, X):
        return np.array([
            np.mean([self.word2vec[w] for w in words if w in self.word2vec] or [np.zeros(self.dim)], axis=0)
            for words in X
        ])
# Train a model
rf_amazon_qa = Pipeline([
    ('word2vec', MeanEmbeddingVectorizer(googlenews_w2v, googlenews_w2v.vector_size)), 
    ('randomforest', RandomForestClassifier(n_estimators=200))])
rf_amazon_qa.fit(X_train, y_train)

5. 評価

最後に、テストデータに関して評価をします。結果は、8割弱の精度となりました。

# Evaluation
y_pred = rf_amazon_qa.predict(X_test)
print(classification_report(y_test, y_pred))
            precision    recall  f1-score   support

          0       0.77      0.81      0.79      2263
          1       0.80      0.76      0.78      2243

avg / total       0.78      0.78      0.78      4506

まとめ

色々調べてたら、とても5分では終わりませんでしたが、一旦わかってしまえば、再現するには5分程度で簡単に出来るのではないかと思います!全体のipynbは、下記にありますので参考まで。

github.com

アーセンベンゲル補完計画、始めました

先月末、ベンゲル監督が二年間の契約延長を発表しました。

www.arsenal.com

今シーズンは、就任以来初じゃないかと思うほどの不振が続いて、契約満了を迎える今シーズンで本当に辞めてしまうかもしれないと思いました。 とりあえず、契約更新は喜ばしいです。 契約更新したとはいえ、いつまでも続けられるわけではない。システム重視で、選手個人の創造性が制約される現代サッカーにおいて、 アタッキングサードでの攻撃には最大限の自由を与えるベンゲルスタイルと、選手は買うものではなく育てるものという哲学は ぜひとも後世に残したい。というわけで「補完計画」始めることにしました。

記者会見のQAからchar-rnnでベンゲル語録を学習

まずは、char-rnnを使って、単語レベルでベンゲル語録を学習し、ベンゲル監督らしい表現の生成に挑戦することとしました。

github.com

データセットは、アーセナル公式のニュースページから取得しました。char-rnnのREADMEによると、2MB以上のテキストデータを 推奨していたので、過去3年くらいのニュースを対象に学習データを作りましたが、結果をみると2MBだと、再現しているとは言い難いですね…

rnn_sizeを128で実行した結果。前後の文脈を考慮するにはサイズが小さいかもしれません。選手名やクラブ名は再現出来てたりはします。

pleing or any other game . is not a great situation . I could get on Saturday . That is what more than that a decision for the lead but as indipphit is that we didn’t stop . I haven got past and let. Nach there are not quickly as the 250 per cent it didn’t take this alent win , to perform , there were worked when he will play a talent achievement and I think overall they made a positive player , I hunnled a humatict inflacke in the game . The players are going to be played as well . You never know whether watching you old and he seemed the ball and when I started very well . We would have Ozil all his game . Once I think , we had quite about twick the keeper we should have not easy at the time in mone big aspects of our game , you could have right until the end of the season . I go for Sagna and Ozil beat Welbeck , we do not live that training after five , I just sait that someone . Cotgion was able to keep toam . We played the five years because we are in a player was ‘ win the league in their good squad well and we played with Olivier is always the day , I also lose us at home and overall I played well . We saw that he was his little to find it , I am surprising is something tellveted to see the new bysical is training . Giroud just very good confirmally . It was a responsiby in the club but let’s just not inflam knowred . On the sched is to the bench comfortably . Sometimes I wanted to do that . We have a big respect for us , he played at half-time and showed that they are disappointed and power or to do . I was success away to Emirates in the way it is a great game and who is great , but it’s a desperate things respects at Tottenham , to go out everybody and it was a very strong test for us and when you don’t improve our young players who is back there happy quich. Hents in the Premier League came on and Man City and we are going . He issunds it finished because we got back to his feet played a little bad . now he had chances away from home they do , t

次は、rnn_sizeを256にして実行した結果。多少のフレーズは生成されるようになった気がします。が、ベンゲル監督の言葉というよりは単にニュースっぽい。というのは、学習データセットで、ベンゲル監督の言葉以外の部分を含んでしまったかも?

oints then you have to keep some demans and they are in where . I had some uncertainties about how can we gain them losing that in the decisive those clubs where we may streak about [ the Liverpool team, I don’t see what the [ampers if you don’t want to just explating , you do not take our characters away . Ospina , the ] hardest physically straight away . has a strange game for the nervous guy ! is not the only thing we can doctict at all that in every day threat . We need to keep this resulting . In the second half the way we want to get a little bit with Chelsea and or the efurition to be a part of it, From South Anegation in the end it was important to him something that makes it on the way that every game and at the end we gave the penalty desire to do it . leader in training , he really not managed to go into the future supporters . , I don’t know whatos.. Alexis Sanchez has been [ Danny Welbeck ] , no matter how export about it . is a fantastic exappoint on six of our defensive players and you see some teams you all are on a visit one and I am confident that Stoke harm for you that about it in the squad last year but I don’t think I cannot completely ready many teams because it was good , and wherea…Buchmestants and finally played well , they were sort of guys like Stoke, we are confident that we ’ll be enough to bring . They were one of the better side today , that’s what we want to do . Szczesny , December 13Gerbracyna Believis , Alex Oxlade-Chamberlain 2-1 Republic or PolandWojciech Szczesny played the full 90 minutes and calm helped Craik Premier League attacled at Rocalem . Consick or £24 ( or 18 ) to know ( will period because he ’s the right movement and into the top close this matchesterscinal because when he hasn’t enjoyed it . ..That ’s a really good cost or not . But after that Theo’s passigh , there was now behind many times . We won all player oncesively fans pressuress .

補完計画の今後

もう少し学習データセットを増やして、rnn_sizeも大きくして試してみたい。 ベンゲル監督の哲学をいつまでも残すために…