桐間紗路研究所日報

技術以外の話→ https://kirimasyaro.hateblo.jp

ツイート形態素解析で幸福度調査/テキストストリーミングの話(gunicorn)

http://aisunyan.moe.hm/chiaki/emo


生活満足度調査


 こんなアプリを作りました

 ツイートを形態素解析して、某所にある単語のポジティブさ・ネガティブさを数値化した表と比較して、ツイートがポジティブなものかネガティブなものかを測る、というフェイクアプリです

 数値の求め方とか、係り受け分析をしてないとか、いろいろ問題はあると思うんですけど、たとえば「好き」という単語を含むツイートをすると数値が上がったり、「雪」という単語で逆に数値が下がりまくったりするのを見てると、まあ面白いんじゃないかなーみたいな

f:id:copuy:20180701141954j:plain

 ここで%になっている数値は、わかりやすいように百分率に直してあるんですけど、実はもうちょっと違う値で、無理やり%にしてるので色々おかしい、というのも課題・・・

開発の話

 
 それより問題なのは、処理に時間がかかるので、処理状況を表示しているんですが、その間ずっとページがロード中になってしまうことなんですね・・・

 こういうのやるときはwebsocketとか使ったほうが良いんだろうなあって思うんだけど、まあとりあえず成果が上がればいいやの精神でとりあえず作ってみました・・・



 このアプリはflask⇔gunicorn⇔nginxという構成でLinuxで動かしているんですけど、それで、処理状況を表示するのにこういうコードを書いています

def stream_template(template_name, **context):
    app2 = Flask(__name__)
    app2.jinja_loader = FileSystemLoader('templates')
    app2.update_template_context(context)
    t = app2.jinja_env.get_template(template_name)
    rv = t.stream(context)
    rv.disable_buffering()
    return rv

@app.route('/chiaki/emo')
def main2():
    if "oauth_token" not in session:
        auth_info = 'ログインしてください'
    else:
        auth_info = 'あなたは現在ログインしています'
    def generate():
        if request.args.get("analyze", None) is not None:
            for x in analyze():
                yield x
        else:
            yield ['', '', '']
    rows = generate()
    return Response(stream_with_context(stream_template('chiaki/emo3.html', auth_info=auth_info, status=rows)))

 Flask: テキスト・ストリーミングやってみた♬
 このページを参考にしています
 Flask(__name__)をわざわざもう一度生成しているのは、このコードがおいてある場所がアプリのメインとなるファイルではなく、Blueprintを使って読み込んでいるため、app.jinja_env.get_templateとやってしまうと、appがflaskではなくblueprintなのでエラーになってしまうためです(説明しにくい・・・

 ポイントになるのはyielddisable_buffering()です。これで処理中にレスポンスを返すことができます。
 しかしここで引っかかりました。テスト環境ではうまくいくのになぜか本番環境でうまくいかない
 そこでこのページを見つけました
 Deploying Gunicorn — Gunicorn 19.8.1 documentation
 If you want to be able to handle streaming request/responses... のところ、
 To turn off buffering, you only need to add proxy_buffering off;
 これに従ってやるとgunicornでストリーミングとしてレスポンスを垂れ流すことができる(?)みたいです

 nginxのconf

  location @my_app {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Request-Id $request_id;
    proxy_redirect off;
    proxy_buffering off;
    proxy_pass http://hello;
    proxy_connect_timeout       3600;
    proxy_send_timeout          3600;
    proxy_read_timeout          3600;
    send_timeout                3600;
  }

こんなふうに設定してあります
同様の問題で困ってる方いたら参考になったら幸いです

ツイートをCSVで約400件バックアップできるサイトを作った(仮)

 

http://aisunyan.moe.hm/hello/tw

 

 アプリを作りました

 FlaskというPythonフレームワークを使って作っています

 改良中なんでたまにバグ起こして止まってると思いますが、できるだけサービスを維持していこうと思います

 サーバー代ほしい

 

 一応使い方

 ・・・といっても、2つのボタンを上から順番に押すだけです

 認証するとセッションとしてoauthキーがブラウザに保存されていて、そうするとログインしている旨が表示されているはずなので、その間にcollectボタンを押してダウンロード

 一応ログアウトもできます

 

 出力されるまで少し時間がかかって、特にWebSocketとかajaxとかはいまのところ使ってないので、読み込み中状態になって気持ち悪いですが、まあ当分はこれで行こうと思います

 

pythonで最小二乗法 その1

 このテーマはありきたりだと思うのでn番煎じという感じですが、メモとして


最小二乗法とは

 ぶっちゃけよく知らないんで雑な説明はしたくないんですが、最低限の説明として、まず2次元直交座標上に表せる点データの集合があったとして、その相関を最もよく説明できる


a=\dfrac{\mathrm{Cov}(X,Y)}{\sigma_X^2} \\
b=\mu_Y-A\mu_X

最小二乗法(直線)の簡単な説明 | 高校数学の美しい物語

 という一次直線(y=ax+b)を求める作業ということで良いとおもいます。式が複雑になると萎えてしまうのでコンパクトなものを引用させていただきました。ありがとうございます。
もちろんこれがn次のデータになっていくとどんどんこれが複雑になっていくわけですが、それはまた今度ってことで。

 こういう直線モデルの最小二乗法では、物理の実験なんかで、実験にはつきものである誤差をなるべく少なくするために使ったりするそうです。

 Covというのは共分散といって、偏差の積の平均のことです。偏差ってのは平均からの差のことです。

 わかりにくいと思うので簡単な例を用いて説明すると、完全に相関(?)する、xが1のときyがx*2である点を任意に3つ選び、その相関を検証します
 点(1,2)、点(3,6)、点(8,16)というデータの共分散は、1,3,8の平均は4で、2,6,16の平均は8ですから、偏差の積は-3と-6の積、-1と-2の積、4と8の積というようになります。その平均は(18+2+32)/3=17.333333...と求められます。これが共分散です。あとσxはxの標準偏差です。標準偏差は求めるのが面倒くさい(手順が多いだけで難しくはありませんが)ので、適当なツールを使って求めると2.94392と出てきますので、それを使用します。これでまず求める直線の式の不明な2変数のうちの一つ、a17.3333/(2.9439^2)で17.3333/8.6665=2.000346....と求められます。ここまでくれば切片bを求めるのは簡単で、yの平均マイナス(xの平均かける切片a)で、8-(2*4) = 0 となります。これで前提であるy=x*2(+0)が導けることがわかったとおもいます。(?)

python

 まあ数学の説明は今回メインじゃないんで、この部分は読み飛ばしといても大丈夫、だとおもいます。で、ここでPythonを使っていきます。せっかくなのでさっきのデータ
 点(1,2)、点(3,6)、点(8,16)
 を使います。
 上記の話は

import numpy as np
data = [[1,2],[3,6],[8,16]]

n = len(data)
x = [x[0] for x in data]
y = [x[1] for x in data]
sigmax = np.std(x)
avex = np.mean(x)
avey = np.mean(y)
cov = sum((x - avex)*(y - avey) / n)
a = cov / sigmax ** 2
b = avey - a * avex

print('傾き = ' , a)
print('y軸切片 = ' , b)


と書くことができます。なお標準偏差(np.std)には不偏標準偏差というのもあるのですが、Pythonのデフォルトではそうではないそうです。あとsumは式の途中にリストがあると自動的に繰り返してくれる(?)っぽいので便利ですね。

f:id:copuy:20180520222821j:plain

 求められました。たぶんできてるとおもいます
 次回からはこれを使って実用的なことをしていきたいとおもいます。