barilog

大学院生が色々なものをクリエイトしてみる!

金融テキストマイニングにおけるモデルの評価

コーパスとテキストマイング

http://amzn.asia/2IbDzDK

の読書メモ

 

 

モデルの評価の基礎的な流れは以下のとおりです.

 

 

1.訓練データからモデルを学習する

2.評価データの説明変数をモデルに入力し評価データの目的変数の値を推測する.

3.実際の評価データの目的変数の値と推測した値とを比較することで予測精度(予測誤差)を計算する.

 

さて金融テキストマイニングでは,データは時系列である点に注意しましょう.未来の訓練データで学習したモデルを過去の評価データに適用しても,予測精度を正しく計算することはできません.

一般のデータマイニングでは予測精度を計算する際に交差検定が用いられますが,上記の理由から金融テキストマイニングには向きません.

(個人的な疑問:これは交差検定ではデータ・セットを過去・未来にかかわらずランダムに選んで数個の訓練データ+評価データの塊に分割するから,なのかな?時系列に沿って分割)

 

予測精度を正しく求めるためには,評価データよりも過去の訓練データから学習したモデルで予測精度を計算しなければなりません.また,モデル選択(複数のモデルからあるモデル-たとえば重回帰や回帰木など-を選んだり,パラメータをチューニングする)のために訓練データをさらに訓練データと検証データに分けることがありますが,この際に検証データも訓練データより過去のものとしなければなりません.

 

この理解でいくと,

もし,単体の時系列データ・セットが渡されたなら,

データを時系列に沿って2つ(検証データが必要であるならば3つ)にわけ,一番古い時系列のデータを訓練データ,(検証データが必要であるならば次に古いものを検証データ,)そして一番最新の時系列データを評価データとする.

というのが一般の流れのようです.当たり前なところなんですが悩む人も多いところななのでまとめました.

 

 

参考文献

http://amzn.asia/2IbDzDK

金融テキストマイニングにおけるモデルの評価

コーパスとテキストマイング

http://amzn.asia/2IbDzDK

の読書メモ

 

 

モデルの評価の基礎的な流れは以下のとおりです.

 

 

1.訓練データからモデルを学習する

2.評価データの説明変数をモデルに入力し評価データの目的変数の値を推測する.

3.実際の評価データの目的変数の値と推測した値とを比較することで予測精度(予測誤差)を計算する.

 

さて金融テキストマイニングでは,データは時系列である点に注意しましょう.未来の訓練データで学習したモデルを過去の評価データに適用しても,予測精度を正しく計算することはできません.

一般のデータマイニングでは予測精度を計算する際に交差検定が用いられますが,上記の理由から金融テキストマイニングには向きません.

(個人的な疑問:これは交差検定ではデータ・セットを過去・未来にかかわらずランダムに選んで数個の訓練データ+評価データの塊に分割するから,なのかな?時系列に沿って分割)

 

予測精度を正しく求めるためには,評価データよりも過去の訓練データから学習したモデルで予測精度を計算しなければなりません.また,モデル選択(複数のモデルからあるモデル-たとえば重回帰や回帰木など-を選んだり,パラメータをチューニングする)のために訓練データをさらに訓練データと検証データに分けることがありますが,この際に検証データも訓練データより過去のものとしなければなりません.

 

この理解でいくと,

もし,単体の時系列データ・セットが渡されたなら,

データを時系列に沿って3つにわけ,一番古い時系列のデータを訓練データ,(検証データが必要であるならば次に古いものを検証データ,)そして一番最新の時系列データを評価データとする.

というのが一般の流れです.

 

論文 Twitter mood predicts the stock market

コーパステキストマイニングより引用

 ---------------------------------------------------------------------------------------------------

 Bollen(2011)は,2008年2月28日から11月28日の9,853,493個のtwitterのテキストを分析し,米国のダウ平均株価指数との関係性を調べた.ユーザー数は約270万人にもなり,1日平均で3.2万個が投稿された.これだけ膨大なテキスト情報があれば,経済に対する世の中の平均的な味方のトレンドが抽出できるのではないかと考えたのである.

 このテキスト情報のうち,彼らは書き手が自分の心的状態を明言していると思われるテキストだけを分析対象とした.そのために,"i feel"や"i am feeling","i'm feeling","i don't feel","I'm","I am","makes me"を含むテキストを抽出した.次に,確実の抽出されたテキストの集合から,どのような心理状態に関連する表現が多いかを指標化した.心理学で使われる気分プロフィール検査(POMS)をべーすとした,Google-Profile of Mood States(GPOMS)指数うを新たに提唱している.元になったPOMSは,被験者に対して現在の自分の心的状態を,「有効的な」「ふきげんな」「活発な」「限界ギリギリの」「パニック状態の」等の72種類の表現への7段階程度の当てはまりを聞く質問紙調査である.この回答を分析し,被験者の心的状態に変換し,被験者の心的状態を,平穏・警戒・確信・活気・善意・幸福の6次元の尺度で表す.GPOMSは,

Gooleの4,5-gram共起語(25億語)(Brants, Franz, 2006; Bergsma et al., 2009)を使用して,POMSの72表現に関連する964語の辞書を用いている.この拡張辞書の頻度により,確実のテキストデータから先程の6次元の尺度のスコアを計算する.

 テキスト情報を取得した2008年2月28日から11月28日について,6次元のGPOMS指数とダウ平均株価指数を用いて,Granger因果性検定を行った.その結果,「平穏」の尺度が2~5日語の平均株価との因果性があった.さらに,1日前から3日前までの「平穏」のスコアと平均株価を入力とsて翌日の平均株価を予測するモデルを,Self-organizing Fuzzy Neural Network(SOFNN)手法を用いて構築した.訓練期間は2008年2月28日から11月28日であり,テスト期間は2008年12月1日から19日である.その結果,翌日の平均株価の騰落の方向性を,86.7%の精度で予測することができた.しかし,テキスト情報を用いずに,過去の3日間の平均株価だけから予測した場合でも,73.3%の予測精度があった. 

 ---------------------------------------------------------------------------------------------------

参考文献

http://amzn.asia/2IbDzDK

https://arxiv.org/pdf/1010.3003.pdf

https://catalog.ldc.upenn.edu/products/LDC2006T13

https://static.googleusercontent.com/media/research.google.com/ja//pubs/archive/35387.pdf

 

BOWのデータフレームを作る簡単な方法

シンプルなテーマだが,あまり情報がなかったので調べてみた.

なんらかテキスト集合からBag Of Words,つまり(単語ID, 出現回数)の集合を作る方法はよく情報提供がなされている.

 

 

よく提供されている方法としてgensimのcorporaを使ったものがあるが,そこで作れるものは[(単語1, 出現回数), (単語2, 出現回数),......]のようにタプル形式であることが多い

 

学習器に入力するとき,扱いやすいpandas.DataFrameオブジェクトに変換したいと思うのだが,corporaのような形式からデータフレームにするのは若干面倒だ.

 

今回は

テキスト情報から簡単にBOWのデータフレームを作る方法を紹介する.

 

結論からいってしまうと


chrisalbon.com


この記事をなぞればいい.

 

ただし,ここで一つ落とし穴がある

>|python|

bag_of_words.toarray()

|

オフラインでもssh接続を維持するために

awsなどのサーバーにssh接続した上で時間のかかる処理をしている場合,

基本的にパソコンをwifi環境から外してしまうとssh接続が途切れる.

 

10時間ぐらいかかるプログラムを回していて終電にぶちあたってしまったときなどは大変だ.

 

そういうときの対策をリサーチしてみた.

 

基本的に

 

moshを使用する
ターミナルの ServerAliveIntervalという設定を長めに設定する(?まだ試していない)

などの対策があるようだ.

 

以下参考

helen.hatenablog.com

qiita.com

ubuntuでpipでインストールしたライブラリを使いたいのにパスが通っていない,,

という場合のの対処法
結論から言えば
以下のように
site-packagesのパスを調べて
sys.pathに追加してあげればOK!!

#ライブラリのパスを設定
import sys
sys.path.append("/home/ubuntu/anaconda3/lib/python3.6/site-packages")


site-packagesのパスは使っている環境によって異なるのでみなさんも自分で調べてみてね.
調べるためには
【Python】site-packagesのパスを確認する方法【pip install】

のやり方を参考にすると良いと思う

mecabによるtokenize中に起こるUnicodeDecodeErrorの解決

import MeCab
#mecab = MeCab.Tagger(' -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd')
mecab = MeCab.Tagger('mecabrc -u expert.dic')

import re

from bs4 import BeautifulSoup

from urllib.request import urlopen

# ストップワードを定義
def make_stopwords():
#英語のストップワード
from nltk.corpus import stopwords
nltk_stopwords = stopwords.words('english')
#日本語のストップワード
slothlib_path = 'http://svn.sourceforge.jp/svnroot/slothlib/CSharp/Version1/SlothLib/NLP/Filter/StopWord/word/Japanese.txt'
slothlib_file = urlopen(slothlib_path)
slothlib_stopwords = [line.decode("utf-8").strip() for line in slothlib_file]
slothlib_stopwords = [ss for ss in slothlib_stopwords if not ss=='']

#その他の独自のストップワード
additional_stopwords = ['rt', '%', 'の', 'ん', '0']
#連結
stopwords = nltk_stopwords + slothlib_stopwords + additional_stopwords

return stopwords


def clean_text(text):
replaced_text = text
#replaced_text = '\n'.join(s.strip() for s in text.splitlines()[2:] if s != '') # skip header by [2:]
replaced_text = replaced_text.lower()
replaced_text = re.sub(r'[【】]', ' ', replaced_text) # 【】の除去
replaced_text = re.sub(r'[()()]', ' ', replaced_text) # ()の除去
replaced_text = re.sub(r'[[]\[\]]', ' ', replaced_text) # []の除去
replaced_text = re.sub(r'[「」]', ' ', replaced_text) # 「」の除去
replaced_text = re.sub(r'[@@]\w+', '', replaced_text) # メンションの除去
replaced_text = re.sub(r'^RT', '', replaced_text) # 文頭にRTがあれば除去
replaced_text = re.sub(r'\d+\.?\d*|\.\d+', '0', replaced_text) # 数値があれば全て0に変換
# replaced_text = re.sub(r'https?:\/\/.*?[\r\n ]', '', replaced_text) # URLの除去
replaced_text = re.sub(r"(https?|ftp)(:\/\/[-_\.!~*\'()a-zA-Z0-9;\/?:\@&=\+\$,%#]+)", "" , replaced_text) #URLの除去
replaced_text = re.sub(r' ', ' ', replaced_text) # 全角空白の除去
return replaced_text


def clean_html_tags(html_text):
soup = BeautifulSoup(html_text, 'html.parser')
cleaned_text = soup.get_text()
cleaned_text = ''.join(cleaned_text.splitlines())
return cleaned_text


def clean_html_and_js_tags(html_text):
soup = BeautifulSoup(html_text, 'html.parser')
[x.extract() for x in soup.findAll(['script', 'style'])]
cleaned_text = soup.get_text()
cleaned_text = ''.join(cleaned_text.splitlines())
return cleaned_text


def clean_url(html_text):
"""
\S+ matches all non-whitespace characters (the end of the url)
:param html_text:
:return:
"""
clean_text = re.sub(r'http\S+', '', html_text)
return clean_text


def clean_code(html_text):
"""Qiitaのコードを取り除きます
:param html_text:
:return:
"""
soup = BeautifulSoup(html_text, 'html.parser')
[x.extract() for x in soup.findAll(class_="code-frame")]
cleaned_text = soup.get_text()
cleaned_text = ''.join(cleaned_text.splitlines())
return cleaned_text

def extract_hashtag(tweet):
out = tweet
no_hashtag_text = ''
hashtag_list =

pattern = r'[##]([\w一-龠ぁ-んァ-ヴーa-z]+)'

for hashtag in ((re.findall(pattern , tweet))):
#ハッシュタグ
hashtag_list.append(hashtag)
r = r'[##]%s' % hashtag
out = re.sub(r, '', out)
no_hashtag_text = out
return no_hashtag_text, hashtag_list

 


def tokenize(text):
'''
とりあえず形態素解析して名詞だけ取り出す感じにしてる
'''
node = mecab.parseToNode(text)
while node:
if node.feature.split(',')[0] == '名詞':
yield node.surface.lower()
node = node.next


def get_words(contents):
'''
記事群のdictについて、形態素解析してリストにして返す
'''
ret =
for k, content in contents.items():


#ハッシュタグの削除
content = extract_hashtag(content)[0]

#不要な文字列の削除
content = clean_text(content)

ret.append(get_words_main(content))
return ret


def get_words_main(content):
'''
一つの記事を形態素解析して返す
ストップワード以外の単語
'''

return [token for token in tokenize(content) if token not in stop_words]


#ライブラリのパスを設定
import sys
sys.path.append("/home/ubuntu/anaconda3/lib/python3.6/site-packages")


df = TweetItem.df.loc[:,['tweet_id','text']]
stop_words = make_stopwords()
words = get_words({row[1]['tweet_id'] : row[1]['text'] for row in df.iterrows()})

#wordsをpickleに保存
import pickle
with open('words.pkl', 'wb') as f:
pickle.dump(words, f)

from gensim import corpora

# words はさっきの単語リスト
dictionary = corpora.Dictionary(words)
#print(dictionary.token2id)

#dictionary.filter_extremes(no_below=20, no_above=0.3)
# no_berow: 使われてる文章がno_berow個以下の単語無視
# no_above: 使われてる文章の割合がno_above以上の場合無視

#辞書をファイルに保存します。
dictionary.save_as_text('tweetdic.txt')
#作った辞書ファイルをロードして辞書オブジェクト作るとき dictionary = corpora.Dictionary.load_from_text('livedoordic.txt')

corpus = [dictionary.doc2bow(text) for text in words]
with open('corpus.pkl', 'wb') as f:
pickle.dump(corpus, f)

 

上記のコード(tweet文書を読み込んで,corpusというBOWを出力する)を動かした結果,


<ipython-input-45-d0315e671431> in tokenize(text)
106 while node:
107 if node.feature.split(',')[0] == '名詞':
--> 108 yield node.surface.lower()
109 node = node.next
110

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa2 in position 2: invalid start byte

 

というエラーが起きた.

 

これの原因はすぐ見つかった.

 

MeCabのバージョン0.996で、この方法によりPython3からMeCabのparseToNode関数を使うと、最初のsurface(表層形)が表示されないというバグがある。

 

 

 

www.trifields.jp

 

こちらを参考にした.

 

 

このバグの最も簡単な回避策は、parseToNode関数を呼び出す前に、一度、parse関数を呼び出すことで回避することができる。
実際に見てみよう。

先ほどのPython3のコードに「t.parse(”)」という一文を追加する。

 とあるので,

mecab = MeCab.Tagger(~~)

の直後に

mecab.parse("")

を挿入して対策完了

 

 

エラーが解消された.