テキストの感情分析

7.3.1

テキストの感情分析

最終更新 2020-01-29 読了時間 3 分
まとめ
  • HuggingFaceのSiEBERTモデルで英文テキストをポジティブ・ネガティブに分類する。
  • 決算発表テキストを文単位で分析し、トーンの推移を定量化する。
  • 感情スコアの集計・可視化で、テキスト全体のセンチメントを把握する。

直感 #

決算発表やアナリスト向けプレゼンテーションのテキストには、経営陣の自信や懸念が言葉遣いに表れます。感情分析(Sentiment Analysis)を使うと、各文がポジティブかネガティブかを数値化でき、テキスト全体のトーンを定量的に評価できます。大量の決算テキストを横断的に分析すれば、市場のセンチメント変化を先行的に捉える手がかりになります。

詳細な解説 #

Hartmann, Jochen and Heitmann, Mark and Siebert, Christian and Schamp, Christina, “More than a feeling: Accuracy and Application of Sentiment Analysis”, International Journal of Research in Marketing(2022)

モデルのセットアップ #

HuggingFace上のsiebert/sentiment-roberta-large-englishを使用します。Google Colab上で使用する場合は事前にtransformersをインストールしてください。

1
2
3
4
5
6
7
8
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from transformers import pipeline

sentiment_pipeline = pipeline(
    "sentiment-analysis", model="siebert/sentiment-roberta-large-english"
)

決算テキストの文単位分析 #

テキスト全体を「.」で区切り、一文ごとに感情分析を適用します。ここでは決算発表でよく使われる表現をサンプルとして使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
transcript = (
    "Revenue for the third quarter came in at 91 million, in line with previous guidance. "
    "Earnings were strong with net income reaching 47 million. "
    "However, operating costs increased significantly due to supply chain disruptions. "
    "We remain cautious about the near-term outlook given macroeconomic uncertainty. "
    "Our long-term growth strategy continues to deliver results. "
    "Cash flow generation was robust, enabling continued investment in key projects. "
    "We are concerned about rising interest rates impacting our debt servicing costs. "
    "The board has approved a dividend increase reflecting confidence in future earnings."
)

ts_list = [ts.strip() for ts in transcript.split(".") if len(ts.strip()) > 20]
scores = sentiment_pipeline(ts_list)

結果をDataFrameで整理 #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
results = pd.DataFrame({
    "text": ts_list,
    "label": [s["label"] for s in scores],
    "score": [round(s["score"], 4) for s in scores],
})
results["signed_score"] = results.apply(
    lambda row: row["score"] if row["label"] == "POSITIVE" else -row["score"],
    axis=1,
)
print(results[["label", "score", "text"]].to_string(index=False))

感情スコアの可視化 #

各文のスコアを棒グラフで描くと、テキスト内のトーンの推移が視覚的に把握できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
fig, ax = plt.subplots(figsize=(10, 4))
colors = ["#10b981" if s > 0 else "#ef4444" for s in results["signed_score"]]
ax.barh(range(len(results)), results["signed_score"], color=colors)
ax.set_yticks(range(len(results)))
ax.set_yticklabels([t[:50] + "..." if len(t) > 50 else t for t in results["text"]], fontsize=8)
ax.set_xlabel("Sentiment Score(+: Positive, -: Negative)")
ax.set_title("文ごとの感情スコア")
ax.axvline(0, color="#64748b", linewidth=0.8)
ax.grid(axis="x", alpha=0.3)
fig.tight_layout()
plt.show()

テキスト全体のセンチメント集計 #

テキスト全体のポジティブ・ネガティブの割合と平均スコアを算出します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
pos_count = (results["label"] == "POSITIVE").sum()
neg_count = (results["label"] == "NEGATIVE").sum()
avg_score = results["signed_score"].mean()

print(f"文数:     {len(results)}")
print(f"Positive: {pos_count} ({pos_count/len(results):.0%})")
print(f"Negative: {neg_count} ({neg_count/len(results):.0%})")
print(f"平均スコア: {avg_score:+.4f}")

# 円グラフで構成比を表示
fig, ax = plt.subplots(figsize=(5, 5))
ax.pie(
    [pos_count, neg_count],
    labels=["Positive", "Negative"],
    colors=["#10b981", "#ef4444"],
    autopct="%1.0f%%",
    startangle=90,
)
ax.set_title("センチメント構成比")
plt.show()

複数テキストの比較 #

複数の決算テキストを分析し、企業間や四半期間のセンチメント変化を比較するのが実務的な活用法です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 各テキストの平均スコアを比較する例
quarterly_scores = {
    "Q1": 0.42,
    "Q2": 0.28,
    "Q3": avg_score,
    "Q4": 0.51,
}

fig, ax = plt.subplots(figsize=(7, 3.5))
bars = ax.bar(quarterly_scores.keys(), quarterly_scores.values(),
              color=["#10b981" if v > 0 else "#ef4444" for v in quarterly_scores.values()])
ax.set_ylabel("平均センチメントスコア")
ax.set_title("四半期ごとのセンチメント推移")
ax.axhline(0, color="#64748b", linewidth=0.8)
ax.grid(axis="y", alpha=0.3)
fig.tight_layout()
plt.show()

分析のヒント #

  • SiEBERTは英語テキスト専用です。日本語の決算テキストを分析する場合は、日本語対応のモデル(例: koheiduck/bert-japanese-finetuned-sentiment)を使用します。
  • 金融テキストに特化したモデル(FinBERTなど)を使うと、一般的な感情分析モデルよりも金融文脈での精度が向上します。
  • 感情スコアの時系列変化と株価リターンの相関を調べると、テキストマイニングの投資への応用可能性を検証できます。
  • 文単位だけでなく、段落単位やセクション単位で集計すると、決算発表のどの部分でトーンが変わったかがわかります。