技術系

Azureの感情分析を試す

技術系
Cute emoticons background vector with diverse feelings in doodle style

こんにちは、なかにしです。

最近はAzureのリソースを試すことにハマってます。

機械学習をやりたくてE資格を取ったのですが、
まずは既存のサービスを見ておこうと思い、色々試している最中です。

今回は AzureのAI群の1つ、感情分析を試して見ようと思います。

Azureの感情分析とは

テキストの感情的なトーンを自動で検出・分類する機能です。

例えば、「このカフェのコーヒーは香りがよくて最高でした。ただ店員さんの対応は少し冷たかったです。」というレビューに対し、以下の判定をしてくれます。

Positive(肯定的)
Negative(否定的)
Neutral(中立)
Mixed(混合)

さらに、詳細なスコアも教えてくれます。

positive=0.500 neutral=0.210 negative=0.280

これにより、大量のレビューの中から効率的に否定的なレビューを集めたり、「ぱっと見は肯定的だが、実は否定的なレビュー」を見逃す確率を抑えたりすることができます。

使用用途としては、
・ABテストの評価
・新商品リリースの評価
・アンケート調査の評価
あたりに使えそうだなと思いました。

やってみた

Azure上で感情分析のリソースを作成する

Azureのリソースから、Foundry > 言語サービスを選択し、作成します。

エンドポイントとキーを取得する

リソースが作成できたら、キーとエンドポイントを確認します。
キーは、1と2のどちらでもOKです。

ローカルで環境構築

今回は、Pythonだけで動かすので、venvを使用して環境構築をしました。

▼ 今回の構成です

▼ requirements.txt

azure-ai-textanalytics==5.3.0
azure-core==1.32.0
python-dotenv==1.0.1

コードを書く

メインのコードを書きます。
APIを叩き、結果をprintするだけの機能です。

レビューを評価する というパターンを想定し、reviews.json からレビューを読み込み、それを感情分析のAPIに渡して評価させるような仕組みにしました。

import json
import os
import sys
from pathlib import Path
from azure.ai.textanalytics import TextAnalyticsClient
from azure.core.credentials import AzureKeyCredential
from dotenv import load_dotenv

REVIEWS_PATH = Path(__file__).parent / "reviews.json"


def build_client() -> TextAnalyticsClient:
    load_dotenv()
    endpoint = os.environ.get("AZURE_LANGUAGE_ENDPOINT")
    key = os.environ.get("AZURE_LANGUAGE_KEY")
    if not endpoint or not key:
        sys.exit("AZURE_LANGUAGE_ENDPOINT と AZURE_LANGUAGE_KEY を .env に設定してください")
    return TextAnalyticsClient(endpoint=endpoint, credential=AzureKeyCredential(key))


def analyze(client: TextAnalyticsClient, documents: list[str], language: str = "ja") -> None:
    response = client.analyze_sentiment(
        documents=documents,
        language=language,
        show_opinion_mining=True,
    )
    for i, doc in enumerate(response):
        print(f"\n--- Document {i + 1} ---")
        print(f"Text     : {documents[i]}")
        if doc.is_error:
            print(f"Error    : {doc.error.code} - {doc.error.message}")
            continue
        print(f"Sentiment: {doc.sentiment}")
        scores = doc.confidence_scores
        print(f"Scores   : positive={scores.positive:.3f} neutral={scores.neutral:.3f} negative={scores.negative:.3f}")
        for s_idx, sentence in enumerate(doc.sentences):
            print(f"  Sentence {s_idx + 1}: {sentence.sentiment} | {sentence.text}")
            for mined in sentence.mined_opinions:
                target = mined.target
                assessments = ", ".join(f"{a.text}({a.sentiment})" for a in mined.assessments)
                print(f"    Opinion -> target='{target.text}' ({target.sentiment}) assessments=[{assessments}]")


def load_reviews(path: Path) -> tuple[list[str], str]:
    if not path.exists():
        sys.exit(f"レビューファイルが見つかりません: {path}")
    data = json.loads(path.read_text(encoding="utf-8"))
    reviews = data.get("reviews", [])
    if not reviews:
        sys.exit(f"{path} の 'reviews' が空です")
    return reviews, data.get("language", "ja")


def main() -> None:
    reviews, language = load_reviews(REVIEWS_PATH)
    client = build_client()
    analyze(client, reviews, language=language)


if __name__ == "__main__":
    main()

▼ .envの中身

AZURE_LANGUAGE_ENDPOINT=https://<your-resource-name>.cognitiveservices.azure.com/
AZURE_LANGUAGE_KEY=<your-key>

▼ reviews.jsonの中身

{
  "language": "ja",
  "reviews": [
    "このカフェのコーヒーは香りがよくて最高でした。ただ店員さんの対応は少し冷たかったです。",
    "新しいスマートフォンはバッテリー持ちが悪いし、カメラも期待外れだった。",
    "天気は曇りです。"
  ]
}

実行結果

私はWindowsなので、以下でvenv環境に入り

.\.venv\Scripts\Activate.ps1

以下で実行します。

python sentiment.py

結果は、以下でした。

--- Document 1 ---
Text     : このカフェのコーヒーは香りがよくて最高でした。ただ店員さんの対応は少し冷たかったです。
Sentiment: mixed
Scores   : positive=0.500 neutral=0.210 negative=0.280
  Sentence 1: positive | このカフェのコーヒーは香りがよくて最高でした。
    Opinion -> (positive) assessments=[最高(positive)]
    Opinion -> (positive) assessments=[最高(positive)]
  Sentence 2: negative | ただ店員さんの対応は少し冷たかったです。

--- Document 2 ---
Text     : 新しいスマートフォンはバッテリー持ちが悪いし、カメラも期待外れだった。
Sentiment: negative
Scores   : positive=0.000 neutral=0.000 negative=1.000
  Sentence 1: negative | 新しいスマートフォンはバッテリー持ちが悪いし、カメラも期待外れだった。
    Opinion -> (negative) assessments=[期待外れ(negative)]

--- Document 3 ---
Text     : 天気は曇りです。
Sentiment: neutral
Scores   : positive=0.020 neutral=0.960 negative=0.010
  Sentence 1: neutral | 天気は曇りです。
    Opinion -> target='天気' (negative) assessments=[曇り(negative)]

人間の直感と感覚が似ているのがすごいですね。

1つ目のレビューに着目すると、後半の「ただ店員さんの対応は少し冷たかったです。」が若干のネガティブになっているが、前半の「このカフェのコーヒーは香りがよくて最高でした。」の「最高」の部分がかなりポジティブなので、部分相殺しつつポジティブが勝っています。

おわりに

感情分析、なかなか面白いですね。

APIはテキストのみ対応ですが、紙のアンケートをOCRして渡したり、音声認識を噛ませてフィジカルAIに組み込んだりと、組み合わせによってはかなり実用性の高い機能だと思いました。

他にもAzure上にAI系のリソースは沢山あるので、1つずつ試してみようと思います!

今回はここまで!
Enjoy Hacking!