Amazon Web Services ブログ

機械学習を用いたライブコンテンツのモデレーション

ライブチャンネルを管理するには、予期せぬコンテンツがないか、人がストリームを監視する必要があります。特に、有害なコンテンツを迅速に検出し、モデレーションのための適切な措置を講じることが重要です。このガイドでは、Amazon Web Services (AWS) のクラウドサービスを使用して、ライブチャンネルから画像を抽出、保存、分析、選別する自動ソリューションを導入し、有害なコンテンツがないビデオストリームを維持するために必要な手順を説明します。
有害と判断されたコンテンツが確認された場合、ライブチャンネルを停止させることが可能です。参考までに、ストリームに期待されるコンテンツが含まれているかどうかを確認する必要がある場合は、AWS 上の機械学習によるブロードキャスト映像監視の自動化ブログをお読みください。

この例では、以下の Amazon Web Services (AWS) サービスを使用しています。

  • Amazon Simple Storage Service (Amazon S3):どこからでも好きなだけデータを取り出せるように作られたオブジェクトストレージで、抽出した画像を保存します。
  • Amazon Simple Notification Service (Amazon SNS):アプリケーション間およびアプリケーションから人への通信を行うフルマネージドなメッセージングサービスで、有害なコンテンツの可能性が検出された場合にストリームの所有者に通知します。
  • AWS Elemental MediaLive:ブロードキャストグレードのライブビデオ処理サービスで、ライブチャンネルをストリーミングし、画像を抽出します。
  • Amazon Rekognition:機械学習による画像・動画解析を自動化するソリューションで、画像を解析し、モデレートされたコンテンツの基準スコアを返します。
  • AWS Lambda:サーバーレスでイベント駆動型のコンピュートサービスで、画像分析を開始し、分析結果に基づいて画像を選別し、コンテンツが不適切な場合はライブチャンネルを停止します。

AWS Elemental MediaLive は、30 秒ごとにビデオストリームから自動的に画像を抽出して Amazon S3 にアップロードします。AWS Lambda 関数が Amazon Rekognition にアップロードされた画像の解析をリクエストします。Amazon Rekognition は、組み込み済みモデルを使用してモデレートされたコンテンツを検出し、各基準の信頼度を AWS Lambda 関数に返します。その後、AWS Lambda 関数は、Amazon Rekognition の出力に基づいて決定を下します。モデレートされたコンテンツが検出された場合、AWS Lambda 関数は AWS Elemental MediaLive チャンネルを更新してチャンネルを停止し、有害なコンテンツ専用の場所に画像をアップロードして Amazon SNS で通知を送信します。不快なコンテンツが確認されなかった場合、AWS Lambda 関数は、AWS Elemental MediaLive チャンネルを変更せずに、画像を無害なコンテンツ専用の Amazon S3 バケットフォルダにアップロードします。

モデレーターによる更なるレビューには、アップロードのステップが必要です。

Lambda 関数は、Python 3.8 ランタイムを使用します。

前提条件

この How-to ガイドを完成させるには、以下にアクセスする必要があります。

  • シェルと Python コマンドを実行するための Linux システム
  • コンテンツのストリーミングと画像の抽出を行う AWS Elemental MediaLive
  • AWS Elemental MediaLive から画像を保存するための Amazon S3
  • Amazon Rekognition の分析を開始し、決定を下すための AWS Lambda
  • 画像解析を行う Amazon Rekognition
  • モデレートされたコンテンツが検出されたときに通知される Amazon SNS
  • AWS のすべてに渡ってきめ細やかなアクセス制御を行う AWS Identity and Access Management (AWS IAM)

はじめに

Amazon S3

Amazon S3 のコンテンツは Amazon Rekognition と同じリージョンにある必要があるため、あなたの Amazon S3 バケットのリージョンで Amazon Rekognition が利用可能か確認するか、Amazon Rekognition をサポートしている AWS リージョンにフレームをエクスポートしてください。Amazon Rekognition が利用可能かどうかは、こちらでご確認ください。

Amazon S3 は、ビデオライブストリームから抽出した画像を保存し、レビューのために画像をアップロードし、代替映像を保存する目的のために使用されます。

Amazon S3 へのアップロードは、ソースとなる Amazon S3 バケットに送信された最新のフレームで実行する AWS Lambda 関数を開始するために使用されます。

あなたのチャンネルは、シングルまたは標準パイプラインを使用することができます。シングルパイプラインは、ユーザーがフレームキャプチャグループに 1 つの宛先 (1 つのAmazon S3 バケット) を設定する必要があります。標準パイプラインは、デュアル出力による弾力性を提供します。そのため、少なくとも 2 つの Amazon S3 バケットを作成する必要があります (AWS Elemental MediaLive は 2 つの宛先が必要です)。この 2 つのバケットは、例えば “my-movies-bucket” と “my-bucket2” のようになります。標準パイプラインは、保存されるデータ量が 2 倍になるため、コストに影響します。

この例では、シングルパイプラインを使用しています。

Amazon SNS

Amazon SNS トピックは、モデレートされたコンテンツが識別されるたびに、イベントの詳細 (ピクチャ名とチャンネル ID) を取得するためにメッセージを発行する必要があります。

Amazon SNS トピックを作成するには、次の手順に従います。

AWS コンソールで、Amazon SNS サービスに移動し、トピックを作成します。

トピックを作成したら、その Amazon Resource Name (ARN) をメモしておきます。

Get SNS topic ARN

AWS Identity and Access Management (IAM)

AWS Lambda 関数は、以下のアクションを実装するための権限が必要です。

  • rekognition:DetectModerationLabels
  • 入力バケットに対する S3:GetObject
  • 解析後に出力バケットに画像を選別する S3:PutObject
  • チャンネルを停止する MediaLive:StopChannel
  • AWS およびオンプレミス上の AWS リソースやアプリケーションの監視・観測サービスである Amazon CloudWatch に機能ログをアップロードするための AWSLambdaBasicExecutionRole (AWS マネージドポリシー)
  • チャンネルの管理チームに通知するための sns:Publish

サービスにアクセスする AWS Lambda 関数には、実行ロール (この例では “lambda-reko-content-detection” と命名) を作成する必要があります。このロールは、信頼関係を介して AWS Lambda に制限されます。AWS IAM ロールの詳細は、AWS IAM のドキュメントに記載されています。

  • AWS コンソールを開き、AWS IAM サービスに遷移し、新しいポリシーを作成します。
  • 次の AWS IAM ポリシーの例の、AWS Elemental MediaLive チャンネル、Amazon S3 バケット、Amazon SNS トピック ARN を置き換えて適合させて、保存します。
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "RekognitionModerationDetection",
            "Effect": "Allow",
            "Action": "rekognition:DetectModerationLabels",
            "Resource": "*"
        },
        {
            "Sid": "S3GetSourceObjects",
            "Effect": "Allow",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::my-movies-bucket/input/*"
            ]
        },
        {
            "Sid": "S3PutOffensiveContent",
            "Effect": "Allow",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::my-bucket2/output/*"
        },
        {
            "Sid": "MediaLiveStopChannel",
            "Effect": "Allow",
            "Action": [
                "medialive:StopChannel"
            ],
            "Resource": [
                "arn:aws:medialive:eu-west-1:XXXXXX:channel:*"
            ]
        },
        {
            "Sid": "SNSPublish",
            "Effect": "Allow",
            "Action": "sns:Publish",
            "Resource": "arn:aws:sns:eu-west-2:XXXXXX:Moderated_content.fifo"
        }
    ]
}
  • 新しいロールを作成します。信頼されたエンティティとして、AWS Lambda サービスを選択します。
  • 上記で作成した許可ポリシーをアタッチします。
  • このロールに名前を付けて保存します。

AWS Elemental MediaLive

AWS Elemental MediaLive コンソールで、AWS Elemental MediaLive チャンネルを編集し、以下の手順で出力グループを追加してください。

  • フレームキャプチャを選択します。
  • 出力先を Amazon S3 バケットに設定し (ファイル名の設定は必須です。この例では “asset.m3u8” ) 、コンテンツ配信ネットワーク設定を “Frame capture S3” に設定します。
  • チャンネルに標準パイプラインがある場合、フレームキャプチャグループに 2 つのデスティネーションを設定します。この 2 つ目の宛先は、この例では使用しません。
  • フレームキャプチャの出力を設定し、解像度とキャプチャの頻度を設定します。

Amazon Rekognition

こちらに記載されているように、既存のモデルを使用します。また、このドキュメントのように独自のモデルを学習させることも可能です。

AWS Lambda

Amazon Rekognition によるコンテンツ解析を開始し、キャプチャしたフレームを正しい宛先 (承認または不快) に移動させます。

  • まず、Amazon S3 (および Amazon Rekognition) と同じ AWS リージョンに、AWS Lambda 関数を作成します。関数は x86 を必要としないため、より安価な arm64 アーキテクチャを使用することができます。
  • 先ほど作成した実行ロールをアタッチします。

関数は以下の設定で十分です。

Configure Lambda function

DesignerAdd trigger をクリックし、AWS Elemental MediaLive によってキャプチャ画像がアップロードされる Amazon S3 バケットとディレクトリを設定します。

Add a trigger to the Lambda function

以下のコードを AWS Lambda のソースコードにコピーし、DestinationBucket (7 行目)、DestinationDirectory (8 行目)、DestinationBucketRegion (9 行目)、ChannelId (11 行目)、ChannelRegion (12 行目)、SNSTopicArn (13 行目) をセットしてください。

import json
import boto3
from datetime import datetime
def lambda_handler(event, context):
    SourceBucket = event['Records'][0]['s3']['bucket']['name']
    SourceKey = event['Records'][0]['s3']['object']['key']
    DestinationBucket = 'my-movies-bucket'
    DestinationDirectory = 'output'
    DestinationBucketRegion = 'eu-west-2'
#Destination key will depend on analysis result. It is defined automatically
    ChannelId = 3843819
    ChannelRegion = 'eu-west-1'
    SNSTopicArn = 'arn:aws:sns:eu-central-1:XXXXXXXXX:medialive_content_analysis'
    RekognitionRegion = event['Records'][0]['awsRegion']
    try:
        rekognition_client = boto3.client('rekognition', region_name = RekognitionRegion)
    except:
        return_message = 'Can not create Rekognition client'
        function_error(return_message)
    try:
        response = rekognition_client.detect_moderation_labels( 
    Image={
        'S3Object': { 
            'Bucket': SourceBucket, 
            'Name': SourceKey 
            #Required if bucket versioning is enabled 
            #'Version': 'string' 
        } 
    },
    #To be tuned 
    MinConfidence=10)
    except:
        return_message = 'Error during Rekognition analysis'
        function_error(return_message)
    #Use current timestamp to set destination filename (see Python datetime documentation for more details)
    DestinationKey = DestinationDirectory+'/'+DestinationExtraDirectory+'/'+datetime.now().strftime("%Y%m%d%H%M%S")+'.jpeg'
    if len(response['ModerationLabels']) != 0:
        analysis_result = 'positive'
        print('Positive found!')
        for i in response['ModerationLabels']:
            print(i)
        try:
            medialive_client = boto3.client('medialive',region_name=ChannelRegion)
        except:
            return_message = 'Can not create MediaLive client'
            function_error(return_message)
        try:
            medialive_response = medialive_client.stop_channel(ChannelId=str(ChannelId))
        except:
            return_message = 'Can not stop live channel: '+str(ChannelId)
            function_error(return_message)
        try:
            sns_client = boto3.client('sns')
        except:
            return_message = 'Can not create SNS client'
            function_error(return_message)
        att_dict = dict()
        att_dict[key] = {'DataType': 'String', 'StringValue': value}
        SNSMessage = (
"""Moderated content found for channel """
+ str(ChannelId)
+ """
Image uploaded to """
+ DestinationBucket
+ """/"""
+ DestinationKey
+ """
Decision taken to stop the channel"""
)
        try:
            sns_response = sns_client.publish(TopicArn=SNSTopicArn,Message=SNSMessage)
        except:
            return_message = 'Can not publish to SNS topic'
            function_error(return_message)
        print('SNS message sent with ID: '+str(sns_response['MessageId']))
    else:
        analysis_result = 'negative'
    DestinationExtraDirectory = analysis_result
    try:
        s3_client = boto3.resource('s3',region_name=DestinationBucketRegion)
    except:
        return_message = 'Can not create S3 client'
        function_error(return_message)
    copy_source = {
            'Bucket': SourceBucket,
            'Key': SourceKey
    }
    try:
        s3_client.meta.client.copy(copy_source, DestinationBucket, DestinationKey)
    except:
        return_message = 'Can not copy image '+SourceBucket+'/'+SourceKey+' to '+DestinationBucket+'/'+DestinationKey
        function_error(return_message)
    return_message = (
"""Analysis completed, result was """ +analysis_result
+ """
For further review, please refer to file """+DestinationBucket+"""/"""+DestinationKey
)
    return {
            'statusCode': 200,
            'body': json.dumps(return_message)
    }
def function_error(return_message):
    return {
            'statusCode': 500,
            'body': json.dumps(return_message)
    }
  • AWS Lambda 関数を保存します。

FFmpeg を使用して、AWS Elemental MediaLive の入力として RTP (Real-time Transport Protocol) ストリームを生成しています。ソースとして任意の RTP ストリームを使用することもできます。

重要な法律上の注意事項:始める前に、ここに記載されている FFmpeg のライセンス条項と法的考察を熟知していることを確認してください。このデモで使用される FFmpeg static build は、ここに記載されている GNU General Public License の第 3 版の下でライセンスされています。

  • AWS Elemental MediaLive のエンドポイントにストリームを送信します。
ffmpeg -re -i Sintel.mp4 -c copy -map 0 -f rtp_mpegts -fec prompeg=l=5:d=20 rtp://1.2.3.4:5000
  • 攻撃的なコンテンツの 1 枚をシミュレートするために、モデレートされたコンテンツを含む画像を Amazon S3 ソースバケットにアップロードします。
  • AWS Lambda 関数が開始され、ライブチャンネルの停止が決定されます。

料金

サービスに関する注意事項

この例では、AWS Elemental MediaLive チャンネルが 30 秒ごとに Amazon S3 に画像をアップロードするため、1 時間に 120 回、1 日に 2,880 回の呼び出しが発生します。

モデレートされたコンテンツをより速く検出するために、より短い期間を設定することができますが、これは呼び出しの数を増やし、コストに影響を及ぼします。

最も長い実行時間は、モデレートされたコンテンツが特定されたときに発生するため、AWS Lambda 関数に追加のステップがあります:AWS Elemental MediaLive チャンネルを停止します。

以下の価格は、AWS US East (Ohio) リージョンの価格に基づいています。他の AWS リージョンを使用する予定の場合は、対象となる AWS リージョンのサービス価格をお読みください。

AWS Lambda

x86 アーキテクチャと arm64 アーキテクチャの両方で同じ実行時間でした。

テスト中の平均課金時間は 1.3 秒でした。

最大課金時間は 2.5 秒でした。

AWS Lambda の無料利用枠には、月あたり 100 万回の無料リクエストと月あたり 40 万 GB 秒のコンピューティングタイムが含まれています。

私たちの AWS Lambda 関数は 30 秒ごとに実行されるため、31 日の 1 ヶ月間で、89,280 回実行されます。

テストの結果、平均実行時間は 1.3 秒、メモリ消費量は 128 MB でした。

その結果、1 ヶ月で合計 116,064 秒 (89,280 回 × 1.3 秒) 実行されることになります。

メモリは 128 MB を使用しているので、1 ヶ月あたりのギガバイト秒の消費は、116,064 秒 × 128 MB ÷ 1024 に相当します。つまり、1 ヶ月あたり 14,508 GB 秒に相当します。

無料利用枠を利用しない場合、AWS Lambda の費用は、14,508 GB 秒 × 0.0000133334 USD/GB 秒 となります。つまり、0.193440967 USD に相当することになります。

1 ヶ月あたりの実施回数は 100 万未満であり、AWS Lambda の無料利用枠は除外したため、リクエストには1 ヶ月あたり 0.20 USD の追加料金がかかり、30 秒の頻度で最大約 0.40 USD (0.193440967 USD + 0.20 USD) /月の費用が発生することになります。

AWS Lambda の価格に関する詳細は、こちらをご覧ください。

Amazon Rekognition

月間の最初の 100 万画像は、1 画像あたり 0.001 USD で課金されます。1 日あたり 2,880 画像とすると、最大請求額は 1 ヶ月あたり 89.280 USD です。

Amazon Rekognition の価格についての詳細は、こちらをご覧ください。

Amazon S3

Amazon S3 と AWS Lambda は同じ AWS リージョンにあるため、このドキュメントにあるように、データ転送料はかかりません。

ストレージはキャプチャの解像度とコンテンツに大きく依存します。1920 × 1080 の解像度、コンテンツ、圧縮を使ったテストでは、AWS Elemental MediaLive は 250 KB 以上のファイルを出力したことはありません。

月間 89,280 枚をベースにすると、総ストレージ量は 21,796.875 MB (89,280 × 250 KB / 1024) に相当します。

キャプチャしたフレームを 1 つの保存先にプッシュし、最初の 50 TB は 1 GB /月あたり 0.023 USD で課金されるので、ストレージコストは 0.501328125 USD となります。

AWS Elemental MediaLive がフレームキャプチャを Amazon S3 にアップロードするように構成されている限り、全体のコストは増加します。画像を別のストレージクラスに移動するか、削除することを検討するとよいでしょう。ここに記載されているように、ライフサイクルポリシーによって、別のストレージクラスへの移行を自動化したり、ファイル削除を自動化したりすることができます。

Amazon S3 の価格に関する詳細は、こちらをご覧ください。

Amazon SNS

最悪の場合、AWS Lambda を呼び出すごとに AWS Elemental MediaLive チャンネルを停止するため、1 ヶ月あたり最大 89,280 件の Application Programming Interface (API) コールが発生します。

サービス料金は、100 万 API コールあたり 0.30 USD、ペイロードデータは 1 ギガバイトあたり 0.017 USD です。

私たちのメッセージは数文字しかないため、この関数では月に数 MB 以上は送信されないでしょう。

Amazon SNS の価格についての詳細は、こちらをご覧ください。

合計

30 秒ごとの画像解析にかかる全体のコストは、ライブチャンネルあたり月額 91.01 USD 以下と見積もることができます。

次の表は、30 秒、10 秒、1 秒ごとのフレームキャプチャに基づく月額コストの詳細です。

Capture frequency AWS Lambda implementation cost Amazon Rekognition cost AWS Lambda invocation cost Amazon S3 cost Amazon SNS cost Total monthly cost
Every 30 seconds (2,880 per month) < 0.20 USD 89.280 USD 0.20 USD 0.50 USD 0.317 USD < 90.5 USD
Every 10 seconds (8,640 per month) < 0.60 USD 267.84 USD 0.20 USD 1.50 USD 0.317 USD < 270.5 USD
Every 1 second (86,400 per month) < 6 USD 2,678.4 USD 0.20 USD 15.04 USD 0.317 USD < 2,700 USD

結論

この記事では、ライブチャンネルでモデレートされたコンテンツを検出し、さらなる分析のためにキャプチャしたフレームを保存し、モデレートされたコンテンツが検出されたときにメディア管理チームに通知し、ライブチャンネルを停止するワークフローを作成しました。このページにアクセスして、AWS AI ソリューションとサービスにおけるマルチモーダルコンテンツモデレーション機能について学び、すべてのサービスを無料でお試し、あなたのユーザー、ブランド、オンラインコミュニティを低いコストで的確に保護する方法をブレインストーミングするために我々のチームに連絡してください。

このワークフローを AWS アカウントに導入し、ライブチャンネルのモデレーションを開始することができます。

詳細はこちら

AWS Media Blogでは、AWS 上の機械学習によるブロードキャスト映像監視の自動化機械学習を使用してユーザー生成コンテンツをフィルタリングし、ブランドを保護する方法 など、コンテンツのモデレーションに関する詳細な情報を提供しています。

Damien Martins

Damien Martins

Damien は AWS のテクニカルアカウントマネージャーで、フランス、パリを拠点としています。

参考リンク
AWS Media Services
AWS Media & Entertainment Blog (日本語)
AWS Media & Entertainment Blog (英語)
AWS のメディアチームの問い合わせ先: awsmedia@amazon.co.jp
※ 毎月のメルマガをはじめました。最新のニュースやイベント情報を発信していきます。購読希望は上記宛先にご連絡ください。
翻訳は SA 井村が担当しました。原文はこちらをご覧ください。