Closed10

Amazon Comprehendで感情分析など

kun432kun432

Comrehendでできることを、ざっとPythonで書いてみる。日本語テキストが対象。

作業ディレクトリ作成

mkdir comprehend-work && cd comprehend-work

Python仮想環境作成。最近はuvをよく使う。

uv venv -p 3.12.8

パッケージインストール

uv pip install boto3 loguru
出力
(snip)
 + boto3==1.36.21
(snip)
kun432kun432

言語の検出

https://docs.aws.amazon.com/ja_jp/comprehend/latest/dg/using-api-sync.html#get-started-api-dominant-language

https://docs.aws.amazon.com/ja_jp/comprehend/latest/dg/example_comprehend_DetectDominantLanguage_section.html

import boto3
from botocore.exceptions import ClientError
from loguru import logger
import sys

logger.remove()
logger.add(sys.stdout, level="INFO")


class ComprehendDetect:
    """Comprehendの検出機能をカプセル化したクラス"""

    def __init__(self, comprehend_client):
        """
        :param comprehend_client: Boto3 Comprehendクライアント
        """
        self.comprehend_client = comprehend_client


    def detect_languages(self, text):
        """
        ドキュメントで使用されている言語を検出する

        :param text: 分析対象のテキスト
        :return: 言語とその信頼度スコアのリスト
        """
        try:
            response = self.comprehend_client.detect_dominant_language(Text=text)
            languages = response["Languages"]
            logger.info("{} つの言語を検出しました", len(languages))
        except ClientError:
            logger.exception("言語の検出に失敗しました")
            raise
        else:
            return languages


if __name__ == "__main__":
    comprehend_client = boto3.client("comprehend")
    comprehend_detect = ComprehendDetect(comprehend_client)
    text = "こんにちは!今日はいいお天気ですね。"
    languages = comprehend_detect.detect_languages(text)
    print(languages)
出力
[
    {
        'LanguageCode': 'ja',
        'Score': 1.0
    }
]

日本語英語混じりだとこうなる

(snip)
    text = "こんにちは!今日はいいお天気ですね。On days like this, it's perfect for watching horse racing."
(snip)
出力
[
    {
        'LanguageCode': 'en',
        'Score': 0.6760018467903137
    },
    {
        'LanguageCode': 'ja',
        'Score': 0.32314276695251465
    }
]

ただ「主要」言語とあるように、それぞれの言語のボリュームによるところがありそうで、以下だと英語のみが返ってきた。

(snip)
    text = "こんにちは!今日はいいお天気ですね。I had no luck at all with my horse racing predictions yesterday, and I lost big... I'll do my best next week!"
(snip)
出力
[
    {
        'LanguageCode': 'en',
        'Score': 0.9592218995094299
    }
]
kun432kun432

名前付きエンティティの検出

https://docs.aws.amazon.com/ja_jp/comprehend/latest/dg/using-api-sync.html#get-started-api-entities

https://docs.aws.amazon.com/ja_jp/comprehend/latest/dg/example_comprehend_DetectEntities_section.html

import boto3
from botocore.exceptions import ClientError
from loguru import logger
import sys

logger.remove()
logger.add(sys.stdout, level="INFO")


class ComprehendDetect:
    """Comprehendの検出機能をカプセル化したクラス"""

    def __init__(self, comprehend_client):
        """
        :param comprehend_client: Boto3 Comprehendクライアント
        """
        self.comprehend_client = comprehend_client


    def detect_entities(self, text, language_code):
        """
        ドキュメント内のエンティティ(人物、場所など)を検出する

        :param text: 分析対象のテキスト
        :param language_code: ドキュメントの言語コード
        :return: エンティティとその信頼度スコアのリスト
        """
        try:
            response = self.comprehend_client.detect_entities(
                Text=text, LanguageCode=language_code
            )
            entities = response["Entities"]
            logger.info("{} 個のエンティティを検出しました", len(entities))
        except ClientError:
            logger.exception("エンティティの検出に失敗しました")
            raise
        else:
            return entities
        

if __name__ == "__main__":
    comprehend_client = boto3.client("comprehend")
    comprehend_detect = ComprehendDetect(comprehend_client)
    text = "はじめまして、山田太郎です。東京都千代田区永田町に住んでいます。連絡は000-1111-2222までお願いします。"
    entities  = comprehend_detect.detect_entities(text, "ja")
    print(entities)
出力
[
    {
        'Score': 0.9986088871955872,
        'Type': 'PERSON',
        'Text': '山田太郎',
        'BeginOffset': 7,
        'EndOffset': 11
    },
    {
        'Score': 0.9310240149497986,
        'Type': 'LOCATION',
        'Text': '東京都千代田区永田町',
        'BeginOffset': 14,
        'EndOffset': 24
    },
    {
        'Score': 0.999465823173523,
        'Type': 'OTHER',
        'Text': '000-1111-2222',
        'BeginOffset': 35,
        'EndOffset': 48
    }
]
kun432kun432

名詞キーフレーズの検出

https://docs.aws.amazon.com/ja_jp/comprehend/latest/dg/using-api-sync.html#get-started-api-key-phrases

https://docs.aws.amazon.com/ja_jp/comprehend/latest/dg/example_comprehend_DetectKeyPhrases_section.html

import boto3
from botocore.exceptions import ClientError
from loguru import logger
import sys

logger.remove()
logger.add(sys.stdout, level="INFO")


class ComprehendDetect:
    """Comprehendの検出機能をカプセル化したクラス"""

    def __init__(self, comprehend_client):
        """
        :param comprehend_client: Boto3 Comprehendクライアント
        """
        self.comprehend_client = comprehend_client


    def detect_key_phrases(self, text, language_code):
        """
        ドキュメント内のキーフレーズ(主題や重要なフレーズ)を検出する

        :param text: 分析対象のテキスト
        :param language_code: ドキュメントの言語コード
        :return: キーフレーズとその信頼度スコアのリスト
        """
        try:
            response = self.comprehend_client.detect_key_phrases(
                Text=text, LanguageCode=language_code
            )
            phrases = response["KeyPhrases"]
            #logger.info("{} 個のキーフレーズを検出しました", len(phrases))
        except ClientError:
            logger.exception("キーフレーズの検出に失敗しました")
            raise
        else:
            return phrases
        

if __name__ == "__main__":
    comprehend_client = boto3.client("comprehend")
    comprehend_detect = ComprehendDetect(comprehend_client)
    text = "昨日の競馬は予想がダメダメで、特に京都はボロ負けでした・・・。今週はいよいよフェブラリーSがあるので頑張りますよ!"
    key_phrases  = comprehend_detect.detect_key_phrases(text, "ja")
    print(key_phrases)
出力
[
    {
        'Score': 0.9998824596405029,
        'Text': '昨日の競馬',
        'BeginOffset': 0,
        'EndOffset': 5
    },
    {
        'Score': 0.9716150760650635,
        'Text': '予想',
        'BeginOffset': 6,
        'EndOffset': 8
    },
    {
        'Score': 0.8270034193992615,
        'Text': '特に京都はボロ負け',
        'BeginOffset': 15,
        'EndOffset': 24
    },
    {
        'Score': 0.999767005443573,
        'Text': '今週',
        'BeginOffset': 31,
        'EndOffset': 33
    },
    {
        'Score': 0.9988004565238953,
        'Text': 'フェブラリーS',
        'BeginOffset': 38,
        'EndOffset': 45
    }
]
kun432kun432

感情分析

https://docs.aws.amazon.com/ja_jp/comprehend/latest/dg/using-api-sync.html#get-started-api-sentiment

https://docs.aws.amazon.com/ja_jp/comprehend/latest/dg/example_comprehend_DetectSentiment_section.html

import boto3
from botocore.exceptions import ClientError
from loguru import logger
import sys

logger.remove()
logger.add(sys.stderr, level="INFO")


class ComprehendDetect:
    """Comprehendの検出機能をカプセル化したクラス"""

    def __init__(self, comprehend_client):
        """
        :param comprehend_client: Boto3 Comprehendクライアント
        """
        self.comprehend_client = comprehend_client


    def detect_sentiment(self, text, language_code):
        """
        ドキュメント内の全体的な感情を検出する

        :param text: 分析対象のテキスト
        :param language_code: ドキュメントの言語コード
        :return: 感情とその信頼度スコアのリスト
        """
        try:
            response = self.comprehend_client.detect_sentiment(
                Text=text, LanguageCode=language_code
            )
            logger.info("検出された主な感情: {}.", response["Sentiment"])
        except ClientError:
            logger.exception("感情の検出に失敗しました")
            raise
        else:
            return response
        

if __name__ == "__main__":
    comprehend_client = boto3.client("comprehend")
    comprehend_detect = ComprehendDetect(comprehend_client)
    text = "こんにちは!今日はいいお天気ですね。昨日の競馬は全然予想がダメダメで、ボロ負けでした・・・。来週は頑張りますよ!"
    sentiment = comprehend_detect.detect_sentiment(text, "ja")
    print(sentiment)
出力
{
  "Sentiment": "POSITIVE",
  "SentimentScore": {
    "Positive": 0.9534604549407959,
    "Negative": 0.004291504621505737,
    "Neutral": 0.035939544439315796,
    "Mixed": 0.0063085430301725864
  },
  "ResponseMetadata": {
    "RequestId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
    "HTTPStatusCode": 200,
    "HTTPHeaders": {
      "x-amzn-requestid": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
      "content-type": "application/x-amz-json-1.1",
      "content-length": "166",
      "date": "Mon, 17 Feb 2025 08:34:34 GMT"
    },
    "RetryAttempts": 0
  }
}

ドキュメント「全体」の感情なので、ミックスされた文章の場合でも、何かしら1つの感情判定となる。

kun432kun432

構文解析

これは日本語は非対応。

https://docs.aws.amazon.com/ja_jp/comprehend/latest/dg/using-api-sync.html#get-started-api-syntax

https://docs.aws.amazon.com/ja_jp/comprehend/latest/dg/example_comprehend_DetectSyntax_section.html

import boto3
from botocore.exceptions import ClientError
from loguru import logger
import sys

logger.remove()
logger.add(sys.stdout, level="INFO")


class ComprehendDetect:
    """Comprehendの検出機能をカプセル化したクラス"""

    def __init__(self, comprehend_client):
        """
        :param comprehend_client: Boto3 Comprehendクライアント
        """
        self.comprehend_client = comprehend_client


    def detect_syntax(self, text, language_code):
        """
        ドキュメントの構文要素を検出する
        構文トークンは、名詞、動詞、感嘆詞などの部分として使用されるテキストの部分です。

        :param text: 分析対象のテキスト
        :param language_code: ドキュメントの言語コード
        :return: 構文トークンとその信頼度スコアのリスト
        """
        try:
            response = self.comprehend_client.detect_syntax(
                Text=text, LanguageCode=language_code
            )
            tokens = response["SyntaxTokens"]
            logger.info("{} 個の構文トークンを検出しました", len(tokens))
        except ClientError:
            logger.exception("構文の検出に失敗しました")
            raise
        else:
            return tokens
        

if __name__ == "__main__":
    comprehend_client = boto3.client("comprehend")
    comprehend_detect = ComprehendDetect(comprehend_client)
    text = "Hello! It's a beautiful day today. I had no luck at all with my horse racing predictions yesterday, and I lost big... I'll do my best next week!"
    syntax  = comprehend_detect.detect_syntax(text, "ja")
    print(syntax)
出力
[
  {
    "TokenId": 1,
    "Text": "Hello",
    "BeginOffset": 0,
    "EndOffset": 5,
    "PartOfSpeech": {
      "Tag": "VERB",
      "Score": 0.676778256893158
    }
  },
  {
    "TokenId": 2,
    "Text": "!",
    "BeginOffset": 5,
    "EndOffset": 6,
    "PartOfSpeech": {
      "Tag": "PUNCT",
      "Score": 1.0
    }
  },
  {
    "TokenId": 3,
    "Text": "It",
    "BeginOffset": 7,
    "EndOffset": 9,
    "PartOfSpeech": {
      "Tag": "PRON",
      "Score": 1.0
    }
  },
  {
    "TokenId": 4,
    "Text": "'s",
    "BeginOffset": 9,
    "EndOffset": 11,
    "PartOfSpeech": {
      "Tag": "VERB",
      "Score": 1.0
    }
  },
  {
    "TokenId": 5,
    "Text": "a",
    "BeginOffset": 12,
    "EndOffset": 13,
    "PartOfSpeech": {
      "Tag": "DET",
      "Score": 1.0
    }
  },
  {
    "TokenId": 6,
    "Text": "beautiful",
    "BeginOffset": 14,
    "EndOffset": 23,
    "PartOfSpeech": {
      "Tag": "ADJ",
      "Score": 0.9999995231628418
    }
  },
  {
    "TokenId": 7,
    "Text": "day",
    "BeginOffset": 24,
    "EndOffset": 27,
    "PartOfSpeech": {
      "Tag": "NOUN",
      "Score": 1.0
    }
  },
  {
    "TokenId": 8,
    "Text": "today",
    "BeginOffset": 28,
    "EndOffset": 33,
    "PartOfSpeech": {
      "Tag": "NOUN",
      "Score": 1.0
    }
  },
  {
    "TokenId": 9,
    "Text": ".",
    "BeginOffset": 33,
    "EndOffset": 34,
    "PartOfSpeech": {
      "Tag": "PUNCT",
      "Score": 1.0
    }
  },
  {
    "TokenId": 10,
    "Text": "I",
    "BeginOffset": 35,
    "EndOffset": 36,
    "PartOfSpeech": {
      "Tag": "PRON",
      "Score": 1.0
    }
  },
  {
    "TokenId": 11,
    "Text": "had",
    "BeginOffset": 37,
    "EndOffset": 40,
    "PartOfSpeech": {
      "Tag": "VERB",
      "Score": 1.0
    }
  },
  {
    "TokenId": 12,
    "Text": "no",
    "BeginOffset": 41,
    "EndOffset": 43,
    "PartOfSpeech": {
      "Tag": "DET",
      "Score": 1.0
    }
  },
  {
    "TokenId": 13,
    "Text": "luck",
    "BeginOffset": 44,
    "EndOffset": 48,
    "PartOfSpeech": {
      "Tag": "NOUN",
      "Score": 0.9999996423721313
    }
  },
  {
    "TokenId": 14,
    "Text": "at",
    "BeginOffset": 49,
    "EndOffset": 51,
    "PartOfSpeech": {
      "Tag": "ADV",
      "Score": 1.0
    }
  },
  {
    "TokenId": 15,
    "Text": "all",
    "BeginOffset": 52,
    "EndOffset": 55,
    "PartOfSpeech": {
      "Tag": "ADV",
      "Score": 0.9999994039535522
    }
  },
  {
    "TokenId": 16,
    "Text": "with",
    "BeginOffset": 56,
    "EndOffset": 60,
    "PartOfSpeech": {
      "Tag": "ADP",
      "Score": 0.9999992847442627
    }
  },
  {
    "TokenId": 17,
    "Text": "my",
    "BeginOffset": 61,
    "EndOffset": 63,
    "PartOfSpeech": {
      "Tag": "PRON",
      "Score": 1.0
    }
  },
  {
    "TokenId": 18,
    "Text": "horse",
    "BeginOffset": 64,
    "EndOffset": 69,
    "PartOfSpeech": {
      "Tag": "NOUN",
      "Score": 1.0
    }
  },
  {
    "TokenId": 19,
    "Text": "racing",
    "BeginOffset": 70,
    "EndOffset": 76,
    "PartOfSpeech": {
      "Tag": "NOUN",
      "Score": 0.9998809099197388
    }
  },
  {
    "TokenId": 20,
    "Text": "predictions",
    "BeginOffset": 77,
    "EndOffset": 88,
    "PartOfSpeech": {
      "Tag": "NOUN",
      "Score": 1.0
    }
  },
  {
    "TokenId": 21,
    "Text": "yesterday",
    "BeginOffset": 89,
    "EndOffset": 98,
    "PartOfSpeech": {
      "Tag": "NOUN",
      "Score": 1.0
    }
  },
  {
    "TokenId": 22,
    "Text": ",",
    "BeginOffset": 98,
    "EndOffset": 99,
    "PartOfSpeech": {
      "Tag": "PUNCT",
      "Score": 1.0
    }
  },
  {
    "TokenId": 23,
    "Text": "and",
    "BeginOffset": 100,
    "EndOffset": 103,
    "PartOfSpeech": {
      "Tag": "CCONJ",
      "Score": 0.9999390840530396
    }
  },
  {
    "TokenId": 24,
    "Text": "I",
    "BeginOffset": 104,
    "EndOffset": 105,
    "PartOfSpeech": {
      "Tag": "PRON",
      "Score": 1.0
    }
  },
  {
    "TokenId": 25,
    "Text": "lost",
    "BeginOffset": 106,
    "EndOffset": 110,
    "PartOfSpeech": {
      "Tag": "VERB",
      "Score": 0.9999998807907104
    }
  },
  {
    "TokenId": 26,
    "Text": "big",
    "BeginOffset": 111,
    "EndOffset": 114,
    "PartOfSpeech": {
      "Tag": "ADJ",
      "Score": 0.9719122648239136
    }
  },
  {
    "TokenId": 27,
    "Text": "...",
    "BeginOffset": 114,
    "EndOffset": 117,
    "PartOfSpeech": {
      "Tag": "PUNCT",
      "Score": 0.9933658242225647
    }
  },
  {
    "TokenId": 28,
    "Text": "I",
    "BeginOffset": 118,
    "EndOffset": 119,
    "PartOfSpeech": {
      "Tag": "PRON",
      "Score": 1.0
    }
  },
  {
    "TokenId": 29,
    "Text": "'ll",
    "BeginOffset": 119,
    "EndOffset": 122,
    "PartOfSpeech": {
      "Tag": "AUX",
      "Score": 0.9999990463256836
    }
  },
  {
    "TokenId": 30,
    "Text": "do",
    "BeginOffset": 123,
    "EndOffset": 125,
    "PartOfSpeech": {
      "Tag": "VERB",
      "Score": 1.0
    }
  },
  {
    "TokenId": 31,
    "Text": "my",
    "BeginOffset": 126,
    "EndOffset": 128,
    "PartOfSpeech": {
      "Tag": "PRON",
      "Score": 1.0
    }
  },
  {
    "TokenId": 32,
    "Text": "best",
    "BeginOffset": 129,
    "EndOffset": 133,
    "PartOfSpeech": {
      "Tag": "ADJ",
      "Score": 0.9997863173484802
    }
  },
  {
    "TokenId": 33,
    "Text": "next",
    "BeginOffset": 134,
    "EndOffset": 138,
    "PartOfSpeech": {
      "Tag": "ADJ",
      "Score": 0.9999744892120361
    }
  },
  {
    "TokenId": 34,
    "Text": "week",
    "BeginOffset": 139,
    "EndOffset": 143,
    "PartOfSpeech": {
      "Tag": "NOUN",
      "Score": 1.0
    }
  },
  {
    "TokenId": 35,
    "Text": "!",
    "BeginOffset": 143,
    "EndOffset": 144,
    "PartOfSpeech": {
      "Tag": "PUNCT",
      "Score": 1.0
    }
  }
]
kun432kun432

レスポンスは概ね500ms以内で返ってくるように見える。この手のAPIとしては速い印象。

このスクラップは2025/02/17にクローズされました