Amazon Comprehend でテキストのセンチメント分析

いつのまにか Amazon Comprehend の感情分析が日本語に対応しているのを見つけたので試してみました。

Amazon Comprehend というのは AWSが提供している自然言語処理の機械学習サービスです。
すでに学習済みのモデルが用意されており、利用者はデータを渡すだけで、感情分析(ポジネガ)や、キーフレーズの抽出ができます。

少し前までは日本語は言語判別のみ対応していて、センチメント分析するには一度対応してる言語に翻訳する必要があったはずなのですが、今は日本語も使えます。

ということで、Pythonからこれを動かしてみましょう。

・準備
AWSのPython SDKである boto3が動くように環境をつくっておきます。
主な作業は、 Comprehend の権限を持ったIAMの作成と、そのAccess Key と Secret Access Keyの設定でしょうか。
(僕は環境変数に入れました。)

これで動くはずなのでやってみます。
ドキュメントはこちらです。
Comprehend — Boto3 Docs 1.14.32 documentation

テキストは青空文庫の走れメロスを拝借しました。
まず、1文のポジネガを判定します。使うメソッドは、 detect_sentimentです。
本当に簡単に使えます。


import boto3

# 試すテキストを準備する
text1 = "メロスは激怒した。必ず、かの邪智暴虐の王を除かなければならぬと決意した。"
text2 = "メロスは、村の牧人である。笛を吹き、羊と遊んで暮して来た。"

comprehend = boto3.client("comprehend")
for t in [text1, text2]:
    comprehend_result = comprehend.detect_sentiment(Text=t, LanguageCode="ja")
    print(t)
    print(comprehend_result["Sentiment"])
    for k, v in comprehend_result["SentimentScore"].items():
        print(f"    {k}: {v}")
    print()

# 以下出力結果
"""
メロスは激怒した。必ず、かの邪智暴虐の王を除かなければならぬと決意した。
NEGATIVE
    Positive: 0.00021381396800279617
    Negative: 0.8228716850280762
    Neutral: 0.17690636217594147
    Mixed: 8.087589776550885e-06

メロスは、村の牧人である。笛を吹き、羊と遊んで暮して来た。
NEUTRAL
    Positive: 0.0011265947250649333
    Negative: 6.331767508527264e-05
    Neutral: 0.9988085031509399
    Mixed: 1.5915205722194514e-06
"""

みて分かる通り、 Positive(肯定的)/ Negative(否定的)/ Neutral(中立的)/ Mixed(混在) の4つの感情の割合を返してくれます。

複数のテキストをまとめて判定する関数も用意されています。
それが、batch_detect_sentimentです。

A list containing the text of the input documents. The list can contain a maximum of 25 documents. Each document must contain fewer that 5,000 bytes of UTF-8 encoded characters.

とある通り、 25個までのテキスト(それぞれ5000バイト未満)を順番に判定してくれます。

走れメロスの中に登場する会話文を順番に判定かけてみましょう。

準備として、以下のコードでメロスの会話文の一覧を取得します。


import requests
from bs4 import BeautifulSoup
import re

# 青空文庫 走れメロスのURL
url = "https://www.aozora.gr.jp/cards/000035/files/1567_14913.html"
response = requests.get(url)
# 文字化け対応
response.encoding = response.apparent_encoding
html = response.text

soup = BeautifulSoup(html)

# ルビを取り除く
for tag in soup.findAll(["rt", "rp"]):
    # タグとその内容の削除
    tag.decompose()

# ルビを取り除いたテキストを取得
text = soup.find(class_="main_text").get_text()

# 改行を消す。
text = text.replace("\r\n", "")
text = text.replace("\n", "")
# 全角スペースを消す
text = text.replace("\u3000", "")

# カッコの内側の文字列を抽出する正規表現パターン
speech_pattern = re.compile("「([^「」]+)」")

# カッコの内側の文字列取得
speech_texts = speech_pattern.findall(text)

全部で62文あるので3回に分けて取得します。
結果は扱いやすい様にPandasのDataFrameに入れておきましょう。


import pandas as pd

sentiment_list = []
positive_list = []
negative_list = []
neutral_list = []
mixed_list = []

for i in range(3):
    target_texts = speech_texts[25*i: 25*(i+1)]
    comprehend_result = comprehend.batch_detect_sentiment(
        TextList=target_texts,
        LanguageCode="ja"
    )
    result_list = comprehend_result["ResultList"]

    for r in result_list:
        sentiment_list.append(r["Sentiment"])

        positive_list.append(r["SentimentScore"]["Positive"])
        negative_list.append(r["SentimentScore"]["Negative"])
        neutral_list.append(r["SentimentScore"]["Neutral"])
        mixed_list.append(r["SentimentScore"]["Mixed"])

# 結果をDataFrameに変換
df = pd.DataFrame(
    {
        "text": speech_texts,
        "sentiment": sentiment_list,
        "positive": positive_list,
        "negative": negative_list,
        "neutral": neutral_list,
        "mixed": mixed_list,
    }
)

こうして、走れメロスの全台詞に対してポジネガのフラグをつけることができました。
ちなみに最もポジティブだったセリフはこちらです。

text ありがとう、友よ。
sentiment POSITIVE
positive 0.998629
negative 1.88228e-05
neutral 0.00135026
mixed 1.96162e-06

そして、最もネガティブだったのがこれ。

text ‘もう、駄目でございます。むだでございます。走るのは、やめて下さい。もう、あの方をお助けになることは出来ません。
sentiment NEGATIVE
positive 0.000276001
negative 0.996889
neutral 0.00283329
mixed 1.40147e-06
Name: 46, dtype: object

全テキストの結果を見ていくと流石に「ん?」と思う様なものもあるのですが、
概ねしっかりと感情分析ができてる様に思います。

LightsailのMySQLに接続する

このブログはAWSのLightsailを使って構築しています。
ちょっと訳あって、そのサーバーのDB(MySQL)に接続したくなったのですが、
少しつまずいたのでメモです。

mysqlは勝手に構築してくれているので初期パスワードに何が設定されているのか、わかりませんでした。

結論から言うと
user : root
pass : bitnami_application_password ファイルの中身
で接続できます。
このwordpressの管理画面と同じ初期パスワードだったんですね。

わかるまで、サーバーにログインし、

\$ mysql
\$ mysql -u user
\$ mysql -u root
など試しましたが、ログインできず。

パスワードとして心当たりがあるのが管理画面のパスワードだけだったので、

\$ mysql -u root -p
とコマンドを打った後、bitnami_application_passwordの中身を入力したら接続できました。

念のため設定変えておこう。

Let’s Encrypt の証明書を自動更新する

このブログのhttps化のために導入したLet’s Encrypt の証明書ですが、90日ごとに期限が切れてしまうそうです。
更新手順が用意されていますが、毎回行うのは面倒なのでそれを自動化します。

手順は、証明書を導入した時と同じbitnamiのドキュメントの下の方にあります。
Generate And Install A Let’s Encrypt SSL Certificate For A Bitnami Application
これの Step 5: Renew The Let’s Encrypt Certificate がそれです。

rootユーザーにsuし、
/etc/lego/renew-certificate.sh
というファイルを作ります。中身はこれです。
ドキュメント中の”EMAIL-ADDRESS” と “DOMAIN” は自分のものに置き換える必要があります。


#!/bin/bash

sudo /opt/bitnami/ctlscript.sh stop apache
sudo /usr/local/bin/lego --email=メールアドレス --domains=analytics-note.xyz --path="/etc/lego" renew
sudo /opt/bitnami/ctlscript.sh start apache

スクリプトファイルを作成したら実行できるように権限を修正します。(ユーザーはrootで実行)


chmod +x /etc/lego/renew-certificate.sh

そして、このスクリプトを cronに設定します。
下記コマンドを実行して編集画面を起動。


sudo crontab -e

最終行に先ほど作ったスクリプトを登録して保存したら完了です。


0 0 1 * * /etc/lego/renew-certificate.sh 2> /dev/null

httpのアクセスをhttpsにリダイレクトする

せっかくhttps通信を使うように証明書を設定しましたが、そのままではhttpでもアクセスできてしまいます。
対応として、httpでアクセスがあったらhttpsのURLへリダイレクトするようにします。

これも人によって方法の流儀があるようですが、bitnami のドキュメントにそって設定します。

Force HTTPS Redirection With Apache

方法は簡単で、設定ファイルを書き換えてapachを再起動するだけです。

書き換える設定ファイルはこれ。
/opt/bitnami/apache2/conf/bitnami/bitnami.conf
DocumentRoot の設定の下に3行追加します。


<VirtualHost _default_:80>
  DocumentRoot "/opt/bitnami/apache2/htdocs"
  RewriteEngine On
  RewriteCond %{HTTPS} !=on
  RewriteRule ^/(.*) https://analytics-note.xyz/$1 [R,L]
  <Directory "/opt/bitnami/apache2/htdocs">

設定したらapachを再起動。ちなみにコマンドはこちらです。


sudo /opt/bitnami/ctlscript.sh restart apache

あとは、httpのURLでアクセスして、httpsページが表示されたらOKです。
Chromの開発者ツールで通信をみるとリダイレクトされていることも確認できます。

LightsailのWordPressサーバーにLet’s Encryptの証明書を設定してhttps通信できるようにする

httpのままだと通信が安全でないなどの警告が出るため、https化を試みます。
本当は利用サービスをAWSに寄せていくため、AWS Certificate Managerを使いたくて、
色々調べていたのですが、結局ここで発行した証明書はサーバーに配置できないことがわかりました。
ロードバランサーに設定するそうですがロードバランサー自体がいいお値段するので断念し、
Let’s Encryptというのを利用することにしました。

各所で色々な人が手順を書いていくれていて、微妙に異なるので正しい手順がわからず混乱していたのですが、
bitnami の公式ドキュメントに、Let’s Encryptを使う手順がありますしたのでここの手順を採用します。

Generate And Install A Let’s Encrypt SSL Certificate For A Bitnami Application

ただし、ドキュメント場では下記のスクリプトを使えば簡単なように書いてありますが、Lightsailのサーバーにはこのファイルが無かったので、代替手順(Alternative Approach)の方を採用します。
/opt/bitnami/letsencrypt/scripts/generate-certificate.sh

注意点としては、X.Y.Zを最新のバージョン番号に読み替えるとか、DOMAINやEMAIL-ADDRESSを正しいものに読み替えて実行することと、
自分の場合は wwwのサブドメインを使っていないので、コマンドに含めないようにすることでしょうか。

Lego Client のインストール

サーバーにssh接続して下記コマンドを入れます。


cd /tmp
curl -s https://api.github.com/repos/xenolf/lego/releases/latest | grep browser_download_url | grep linux_amd64 | cut -d '"' -f 4 | wget -i -
# ドキュメント場の記載
# tar xf lego_vX.Y.Z_linux_amd64.tar.gz
# 実際は、curlで取得されたファイルに合わせて、バージョン番号を入れる
tar xf lego_v1.2.1_linux_amd64.tar.gz
sudo mv lego /usr/local/bin/lego

自分のドメインの証明書を作成する

Bitnami servicesを全て止める


sudo /opt/bitnami/ctlscript.sh stop

legoを実行。


# ドキュメントの記載
# sudo lego --email="EMAIL-ADDRESS" --domains="DOMAIN" --domains="www.DOMAIN" --path="/etc/lego" run
# 実際に打ったコマンド(ただしメールアドレスは伏せます)
sudo lego --email=メールアドレス --domains=analytics-note.xyz --path="/etc/lego" run

証明書を利用するようにサーバーを設定する

Apache用の手順を使用します。


sudo mv /opt/bitnami/apache2/conf/server.crt /opt/bitnami/apache2/conf/server.crt.old
sudo mv /opt/bitnami/apache2/conf/server.key /opt/bitnami/apache2/conf/server.key.old
sudo mv /opt/bitnami/apache2/conf/server.csr /opt/bitnami/apache2/conf/server.csr.old
# sudo ln -s /etc/lego/certificates/DOMAIN.key /opt/bitnami/apache2/conf/server.key
sudo ln -s /etc/lego/certificates/analytics-note.xyz.key /opt/bitnami/apache2/conf/server.key
# sudo ln -s /etc/lego/certificates/DOMAIN.crt /opt/bitnami/apache2/conf/server.crt
sudo ln -s /etc/lego/certificates/analytics-note.xyz.crt /opt/bitnami/apache2/conf/server.crt
sudo chown root:root /opt/bitnami/apache2/conf/server*
sudo chmod 600 /opt/bitnami/apache2/conf/server*

お恥ずかしながら、DOMAIN.key や DOMAIN.crtのDOMAIN を見落とし、置き換えないまま実行してしばらくここで詰まりました。

Bitnami servicesを再開


sudo /opt/bitnami/ctlscript.sh start

アクセステスト

https://analytics-note.xyz/
にアクセスします。
正常につながったのでOKです。

一旦設定は完了ですが、このままでは、
httpでも普通にアクセスできてしまうのと、
数ヶ月おきに証明書の有効期限が切れる問題があるのでそれぞれ対応します。

Lightsailにドメインを設定する

Route53で取得したドメインをこのサーバーに紐付けます。

ちなみにドメインの登録については最大3日かかると書いてありましたが、
メールアドレスの認証が済んだら数分後には完了し、
Route53のResistered domains に表示されていました。

ドメインは登録しただけだと使えないので作業を進めます。
公式ドキュメントはこちら
Amazon Lightsail の DNS ゾーンを作成する

DNSゾーン作成手順

  1. Lightsailのホーム画面で、[ネットワーク]を選択。(ドキュメントにはDNS ゾーンと書いてあるが)
  2. DNSゾーンの作成をクリック
  3. ドメイン名を入力
  4. [DNSゾーンの作成]をクリック

この段階では、Wordpressのサーバーと、DNSゾーンがそれぞれ独立して存在するだけで、
このドメインでブログにアクセスすることはできないようです。
続けてレコードの追加という作業が必要になります。

DNS ゾーンにレコードを追加する手順

  1. +レコードの追加をクリック
  2. Aレコードを作成。解決先にblogの固定IPアドレスを入れる(プルダウンから選べる)

Aレコードを作成するとき、サブドメイン無しのURLを使いたい場合は、
サブドメインに@を入れるといいようです。(ポップアップの吹き出しで指定されました)

他のサイトをみてみると、 www.ドメイン名 を サブドメインを含まないドメイン名のみのアドレスに
紐づける設定(CNAMEレコード)を設定していることが多いようですが、
必要性がなさそうなのでそれは設定しませんでした。

この後、画面に表示されている通り
ドメインプロバイダにネームサーバーを設定してあげる必要があります。
Lightsail側に4個表示されているので、これをコピーしてRoute53に持っていきます。

Route53のドメインにネームサーバーを設定

  1. Route53の管理画面にアクセスし、ドメインを選択
  2. Add or edit name serversをクリック
  3. 設定済みのものを消して書き換える
  4. [update]をクリック

Your request for update nameserver was successfully submitted. You will receive an email when it is done.
と表示されたので、メールを待ちます。

少し長めに待ち時間がかかって、ドメイン名でアクセスできるようになりました。

wordpressの管理画面にも自サイトのURLを指定するところがあるので設定変更が必要、
と思っていたのですが勝手に反映されていました。
Lightsailの機能でしょうかね。

Amazon Route 53 を利用して新規ドメインを取得する

このブログで使用するドメインを取得します。
現状、ブログ構築ブログみたいになっていますが、
早々にデータ分析に関する話をメインにしたいのでそれっぽいドメインを取ります。

色々な管理をAWSに集約したいので、お名前.com等ではなく、
Amazon Route 53で取得します。
公式ドキュメントはこちら。
新しいドメインの登録

手順

  1. AWS マネジメントコンソール にサインイン
  2. Route 53 コンソールを開く
  3. [Domain Registration] の [Get Started Now] を選択
  4. [Register Domain] をクリック
  5. Choose a domain name で登録したいドメインを入力
  6. checkをおして、登録できること(StatusがAvailable)を確認
  7. [Add to Cart]をクリック
  8. 登録する年数を選択
  9. [Continue] をクリック
  10. Registrant Contactに自分の情報を登録
  11. [Continue]をクリック
  12. I have read and agree to the AWS Domain Name Registration Agreement にチェック
  13. [Complete Purchase]をクリック

ドメイン登録が完了するまでに最大3日かかるそうです。
(Domain registration might take up to three days to complete.)

ドメインの一覧画面に行くと、Statusが
Domain registration in progress になっていました。

数分後に入力したメールアドレスの確認メールが届きましたので、
中のリンクをクリックして待ちます。

Amazon Lightsail で WordPressサイトを作成する手順

要するにこのブログのサーバーを立てたときのメモです。
ある程度記事が貯まるまでは集客もSEOも考えてないので数日はドメイン無しで運用中。

公式ドキュメント

元々の手順が簡単なうえ、上記の各ドキュメントがわかりやすいのでほぼ迷わず作業完了しました。

サーバー作成手順

  1. AWSコンソールにログイン
  2. 全てのサービスのコンピューティングにある Lightsail を選択
  3. インスタンスタブの[インスタンスの作成]ボタンをクリック
  4. インスタンスロケーションを確認(東京)
  5. インスタンスイメージの選択画面で、下記の通り選択
    • Linux/Unix
    • アプリ+OS
    • wordpress
  6. インスタンスプランの選択 (今回は一番安いのを選択)
  7. インスタンスに名前を付ける
  8. [インスタンスの作成]ボタンをクリック

静的IP作成手順

  1. Lightsailの管理画面で、ネットワークタブを選択
  2. 静的IPの作成ボタンをクリック
  3. 静的 IP をアタッチする Lightsail リソースを選択
  4. 静的 IP に名前を付け、[作成] をクリック

アクセスとログイン

Lightsailのホーム画面から作成したインスタンスを選ぶと、作成したIPアドレスを確認できます。
ブラウザのURLにIPアドレスを入力すると、 自分のwordpress画面にアクセスできる。

また、Lightsailのホーム画面からサーバーにブラウザ上でSSH接続可能。
Lightsail の右上のアカウントから、SSHキーをダウンロードし、接続することもできる。

SSH接続するとホームディレクトリに
bitnami_application_password
というファイルがあり、この中にログインパスワードがある。
ちなみにユーザー名は user

関係ないですが、以前 RedmineをLightsail で立てたときに
初期生成されているユーザー名がわからず苦戦したことがあります。