stMind

about Tech, Computer vision and Machine learning

コンパイルエラーで個人的にわりとよく使うコマンド

OSX El Capitan 10.11.6です。

locate, export

例えば、とあるmakeを実行したらwchar.hが見つからないと言われました。

$ make
g++ libRegionBPP/libRegionBPP.cpp -c -std=c++98 -pedantic -W -Wall -fopenmp -O3 -fPIC
In file included from /usr/local/Cellar/gcc@4.9/4.9.3/include/c++/4.9.3/bits/postypes.h:40:0,
                 from /usr/local/Cellar/gcc@4.9/4.9.3/include/c++/4.9.3/iosfwd:40,
                 from /usr/local/Cellar/gcc@4.9/4.9.3/include/c++/4.9.3/ios:38,
                 from /usr/local/Cellar/gcc@4.9/4.9.3/include/c++/4.9.3/ostream:38,
                 from /usr/local/Cellar/gcc@4.9/4.9.3/include/c++/4.9.3/iostream:39,
                 from libRegionBPP/libRegionBPP.cpp:29:
/usr/local/Cellar/gcc@4.9/4.9.3/include/c++/4.9.3/cwchar:44:19: fatal error: wchar.h: No such file or directory
 #include <wchar.h>
                   ^
compilation terminated.
make: *** [libRegionBPP.a] Error 1

インクルードディレクトリにパスが通ってないのでしょう。まずは、wchar.hの場所を探して、そこにパスを通すことにします。

$ locate wchar.h
...
/usr/include/wchar.h
...
$ export CPATH=$CPATH:/usr/include

設定出来たらもう一度makeです。

$ make
g++ libRegionBPP/libRegionBPP.cpp -c -std=c++98 -pedantic -W -Wall -fopenmp -O3 -fPIC
In file included from /usr/include/string.h:176:0,
                 from libRegionBPP/libRegionBPP.cpp:34:
...
/var/folders/bq/xh66g4d17rn1xkjsmj4md4qm0000gn/T//cczqQzWW.s:3576:11: note: change section name to "__const"
        .section __DATA,__const_coal,coalesced
                 ^      ~~~~~~~~~~~~
ld: library not found for -lSystem
collect2: error: ld returned 1 exit status
make: *** [prog_dSP] Error 1

locate, export, ln

ライブラリが見つからないのだが、これもライブラリディレクトリにパスが通ってないのでしょう。

$ locate libSystem.dylib
...
/usr/lib/libSystem.dylib
$ export LIBRARY_PATH=$LIBRARY_PATH:/usr/lib
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib

再度makeしてみるも同じエラーになって、/usr/local/libも加えたりしてみたものの解消せず。根本的な解決になってないのだけど、makeファイルの中でカレントディレクトリが動的ライブラリの検索パスになっているので、シンボリックリンクをカレントディレクトリに作成することに。

$ ln -s /usr/lib/libSystem.dylib libSystem.dylib

otool

まだまだmakeは続く。で、ここでは、これもよくみるSymbol not found

$ make
...
/Applications/MATLAB_R2015a.app/bin/mex -v -cxx CC='g++' CXX='g++' LD='g++' -output structuredPrediction structuredPrediction.o -L. -lSPRegion -lDH -lRegionBPP -lgomp
dyld: Symbol not found: __ZN5boost13log2_mt_posix10basic_coreIcE11open_recordERKNS0_19basic_attribute_setIcEE
  Referenced from: /Applications/MATLAB_R2015a.app/bin/maci64/libmwfl.dylib
  Expected in: /usr/local/lib/libboost_log.dylib
 in /Applications/MATLAB_R2015a.app/bin/maci64/libmwfl.dylib
/Applications/MATLAB_R2015a.app/bin/mex: line 365: 87163 Trace/BPT trap: 5       $MATLAB/bin/maci64/mex $args
make: *** [structuredPrediction.mexa64] Error 133

otoolでlibmwfl.dylibの依存関係を調べて、

$ otool -L /Applications/MATLAB_R2015a.app/bin/maci64/libmwfl.dylib 
/Applications/MATLAB_R2015a.app/bin/maci64/libmwfl.dylib:
    @rpath/libmwfl.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libcrypto.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)
    @rpath/libmwresource_core.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libmwi18n.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libmwMATLAB_res.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libboost_date_time.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libboost_filesystem.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libboost_log.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libboost_regex.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libboost_serialization.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libboost_signals.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libboost_system.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libboost_thread.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libtbb.dylib (compatibility version 0.0.0, current version 0.0.0)
    /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)

rpathになってて、どこのライブラリがリンクされてるかはわからなかったけど、よくよくエラーを見てみると、/Applications/MATLAB_R2015a.app/bin/maci64/libmwfl.dylibなのに、/usr/local/lib/libboost_log.dylibが使われているから?locateでlibboost_log.dylibの場所を探す。

$ locate libboost_log.dylib
/Applications/MATLAB_R2015a.app/bin/maci64/libboost_log.dylib
/usr/local/Cellar/boost/1.57.0/lib/libboost_log.dylib
/usr/local/Cellar/boost/1.59.0/lib/libboost_log.dylib
/usr/local/Cellar/boost/1.64.0_1/lib/libboost_log.dylib
/usr/local/lib/libboost_log.dylib

そういえば、/usr/local/libをLD_LIBRARY_PATHに設定してしまってたな。/usr/local/libは外して、makeするとようやくコンパイル出来た。

まとめ

これだけでは解決しないことも多いけど、locate, export, ln, otoolくらいで出来る場合も多い印象です。

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