薄っぺらりん

厚くしていきたい

AWS 手軽にできる機械学習モデルの作成と、それを使用した労働災害予防システムを考える

目次

はじめに

この記事はAWS-GUILD Advent Calendar 2023の11日目の記事です。

先日、AWS Skill Builderにて「Use Amazon SageMaker Canvas to make your first ML Model (Japanese)」というラボを行いました。 このラボはAmazon SageMaker Canvasを使用して機械学習モデルを作成し、小売店の顧客維持に有効なキャンペーンを予測するという内容です。SageMaker Canvasは深い機械学習の専門知識を必要とせず、コードを1行も書くことなく機械学習モデルを作成することができるサービスです。ラボではモデルの作成とテストまでを扱っており、SageMaker Canvasの基本的な用途や使い方を学ぶことができます。
その機械学習モデル作成の容易さから、今まで距離を感じていた機械学習の利用がグッと近づいてきたように感じました。様々な用途に使えそうで想像が膨らみますが、今回は労働災害、中でも作業中の熱中症を予防することへの利用を考えてみました。

Amazon SageMaker Canvas機械学習モデルを作る

始めに書いておくべきこととして、私は機械学習について初心者であり、学部時代に研究室で「ニューラルネットワークと深層学習」と、オライリーの「ゼロから作るDeep Learning」をもとに少し学んだ程度の知識しか有していません。

最初にAmazon SageMaker Canvasの概要をサクッと説明してから、今回の核となる熱中症を予測する機械学習モデルを作成します。

SageMaker Canvasとは

Amazon SageMaker CanvasAWSのサービスのひとつで、機械学習に対する専門知識を必要とせず、ノーコードでの機械学習モデル作成が行えるサービスです。
以下は公式ドキュメントからの引用になります。

データソースの接続、データセットの選択、データの準備を行ったら、予測する特徴量を選択して、モデル作成ジョブを開始できます。SageMaker Canvas は、問題のタイプを識別して関連性の高い新しい特徴量を生成し、問題の種類に基づいて、(線形回帰、ロジスティック回帰、深層学習、時系列予測、勾配ブースティングなどの ML 手法を使用して) 何百もの予測モデルをテストして、データセットに基づいて最も正確な予測を行うモデルを作成します。

aws.amazon.com

また、SageMaker Canvasについて手を動かしながら学ぶには、前述のSkill Builderのラボの他に、「Amazon SageMaker キャンバスイマージョンデイ」と言うAWSが提供している以下のワークショップがおすすめです。

catalog.workshops.aws

モデルの学習に利用する熱中症のデータ

今回はシステム化を視野に入れているため、現実として集められそうなデータをもとに学習データセットを作成してみました。
作成したデータセットと作成用プログラムは以下になります。

Training data for heatstroke prediction · GitHub

データセットは次の列を持ち、10000レコード作成しました。

No. 列名 説明
1 month
2 age 年齢
3 sleeping_hours 睡眠時間
4 has_breakfast 朝食食べたかどうか
5 weather 天気
6 10_temperature 9-10時の気温
7 10_wind_speed 9-10時の風速
8 10_humidity 9-10時の湿度
9 10_body_temperature 9-10時の体温
10 10_heart_rate 9-10時の心拍数
11 10_operation 9-10時の作業種類
12 10_hydration 9-10時に水分補給したかどうか
13 11_temperature 10-11時の気温
14 11_wind_speed 10-11時の風速
15 11_humidity 10-11時の湿度
16 11_body_temperature 10-11時の体温
17 11_heart_rate 10-11時の心拍数
18 11_operation 10-11時の作業種類
19 11_hydration 10-11時に水分補給したかどうか
20 12_temperature 11-12時の気温
21 12_wind_speed 11-12時の風速
22 12_humidity 11-12時の湿度
23 12_body_temperature 11-12時の体温
24 12_heart_rate 11-12時の心拍数
25 12_operation 11-12時の作業種類
26 12_hydration 11-12時に水分補給したかどうか
27 heatstroke 午後に熱中症になったかどうか

1レコードが1人のある日のデータを表します。
1~5列目までは基本的な情報です。
6~12列目は作業開始後、9時から10時までの各項目の中央値です。
同様に12~19列目は10時から11時、20~26列目は11時から12時の値です。
27列目にその日の午後に熱中症になったかどうかの結果がyes, noで入ります。

このデータをもとにモデルを作成することで、午前中の環境や状態、行動からその日の午後に熱中症になるかどうかを予測できるのではないかと考えました。

モデルを作成する

上記データを使用してSageMaker Canvasでモデルを作成します。
SageMakerを初めて使うとき、ドメインのユーザーを作成しますが、今回は以下2つの設定を有効にしておきます。

ユーザーのML Ops設定

データセットとして先ほどの熱中症データを読み込むと、モデル作成を行うための設定画面が表示されます。
Target columnにはモデルで予測したいheatstroke列を選択します。全10000データの内26.91%がyesでした。
今回のデータではModel typeに2 category predictionが選ばれました。
Quick buildボタンを押すとモデルの作成が始まります。
ちゃんとしたモデルを作るためにはStandard buildを選択する必要がありますが、モデル作成まで数時間かかり、SageMaker Canvasはログインしているだけでも課金が生じるため、今回はQuick buildにします。

Buildステップ

これでモデルは完成です。今回は約8分で完了しました。
作成されたモデルは88.656%の精度で午後の熱中症を予測できるようです。
午後熱中症になる1番の要因は10_hydrationなので、9時~10時の間に水分補給をしていない場合は熱中症になりやすいようです。
次いで11時~12時の水分補給が影響し、その後11時~12時の気温、10時~11時の気温が続きます。

Analyzeステップ

Predictタブでモデルを使った予測ができます。
適当な入力データで予測してみると、71.992%で午後に熱中症になるかもしれないようです。

Predictタブ

モデルをSageMaker EndpointへデプロイしてAPIから利用する

Deployタブから推論用コンテナにモデルをデプロイし、SageMakerエンドポイントを作成することができます。
コンテナのインスタンスタイプとインスタンス数を指定してデプロイします。

モデルのデプロイ

デプロイが完了するとエンドポイントが作成されます。

Deployments

ここで作成したエンドポイントはSageMakerコンソールのエンドポイント画面からメトリクスなどが確認できます。

Endpoint

作成されたエンドポイントにAPIリクエストを行うことで推論を行うことができます。
次のプログラムでエンドポイントにリクエストしてみます。

以下の出力が得られ、96.905%で午後に熱中症となる結果が得られます。

yes,0.969050407409668,"[0.03094959259033203, 0.969050407409668]","['no', 'yes']"

サーバーレスエンドポイントとしてデプロイする

SageMaker Canvasから直接デプロイした場合、エンドポイントタイプがリアルタイムとなるため、使用していないときも料金が生じます。
エンドポイントタイプには他にサーバーレスも用意されており、用途によってはこちらの方が適している場合があります。
エンドポイントタイプの選び方は「もう悩まない! 機械学習モデルの デプロイパターンと戦略」の35ページの図が参考になります。

今回はリアルタイム推論は不要なので、サーバーレスタイプのエンドポイントを作成することにします。
まずモデルの仕様を調べるため、SageMaker Canvasで作成したモデルをModel Registryに登録します。
右上メニューのAdd to Model Registryから登録できます。

Model Registryへ登録

次にSageMaker Studioを開き、モデルのステータスをApprovedへ変更します。

モデルステータス変更

AWS CLIからdescribe-model-packageを実行し、モデルの推論仕様を取得します。

$ aws sagemaker describe-model-package --model-package-name 'arn:aws:sagemaker:ap-northeast-1:<略>' --profile <profile_name>

docs.aws.amazon.com

出力のInferenceSpecificationが推論仕様です。

"InferenceSpecification": {
        "Containers": [
            {
                "Image": "763104351884.dkr.ecr.ap-northeast-1.amazonaws.com/autogluon-inference:0.4.3-cpu-py38-ubuntu20.04",
                "ImageDigest": "sha256:f461d5f846f9e2f30db9ee57ff6fa6c8ba2263f8cf343f031e1c178a6b6af864",
                "ModelDataUrl": "s3://sagemaker-ap-northeast-1<略>/model.tar.gz",
                "Environment": {
                    "SAGEMAKER_DEFAULT_INVOCATIONS_ACCEPT": "text/csv",
                    "SAGEMAKER_INFERENCE_OUTPUT": "predicted_label",
                    "SAGEMAKER_INFERENCE_SUPPORTED": "predicted_label,probability,probabilities,labels",
                    "SAGEMAKER_PROGRAM": "tabular_serve.py",
                    "SAGEMAKER_SUBMIT_DIRECTORY": "/opt/ml/model/code"
                }
            }
        ],
        "SupportedContentTypes": [
            "text/csv"
        ],
        "SupportedResponseMIMETypes": [
            "text/csv"
        ]
    }

これでエンドポイント作成に必要な情報が揃いました。
今回はQuick buildでモデルを作成しているため、SageMakerにモデルが登録されていません。
なのでまずSageMakerのモデルを作成し、その後エンドポイント設定を作成、そしてエンドポイントを作成という流れになります。
SageMakerコンソールからモデルを作成します。

SageMakerの左のメニュー

コンテナの定義の各項目に先ほど調べた推論仕様を入力します。
環境変数はアウトプットを変更するためにSAGEMAKER_INFERENCE_OUTPUTpredicted_label,probability,probabilities,labelsに設定しておきます。

モデル作成のコンテナ定義

エンドポイント設定ではエンドポイントのタイプからサーバーレスを選びます。
また、最大同時実行数はデフォルトで20になっていますが、アカウント毎のサーバレスエンドポイントのクォータが10なので編集します。

エンドポイント設定

最後にエンドポイントを作成して完了です。
タイプがサーバーレスになっています。

サーバーレスエンドポイント

プログラムからAPIリクエストを行い、推論できることを確認します。
入力データが同じなので、同じ結果になることが確認できます。

yes,0.969050407409668,"[0.03094959259033203, 0.969050407409668]","['no', 'yes']"

熱中症予防システムを考える

モデルの作成とAPIからの推論が確認できたので、ここからはそれを利用したシステムを考えてみます。

システムの利用シナリオ

以下の利用シナリオは、実際はそんなに簡単なことではないと思いますが、ひとつのアイデアとして考えてみたものです。

はじめに、このシステムを導入する会社は屋外での作業を請け負っていると仮定します。何か重いものをひたすら運んだり、何か重いものを組み立てたりするような作業です。
従業員はチームに分けられ、事前に計画されたスケジュールに従い、毎日異なる場所へ車で移動して仕事を行います。
お昼休みは12時~13時です。
気温が高い日などは熱中症で倒れる従業員が出ることがあり困っていました。

会社は熱中症を予防するためにこのシステムを導入します。
従業員に体温や心拍数が分かるセンサーを装備させ、作業場所へ移動する車には気温や湿度が分かるセンサーを搭載します。
各センサーからは数分置きにデータがシステムへ送信されます。

午前の仕事の最後、お昼休みに入る際にチームに支給されている端末から水分補給などの状態を入力しシステムへ送信します。
するとシステムが午後に熱中症で倒れそうな従業員がいることをチームリーダーに通知します。
チームリーダーはその通知を参考に当該従業員の作業割り当てを変更したり、作業から外して休ませるなどの対策を行うことができます。

アーキテクチャ

次のようなアーキテクチャを考えてみました。

熱中症予防システムのアーキテクチャ

センサーからの生データはS3バケットに保存します。
DBには各従業員の基本情報や、作業スケジュール、作業場所などの情報が保存されており、作業中に水分補給したかどうかも別システムからここに記録されるものとします。

図中の番号に応じた流れは次のようになります。

  1. 人や車に装備したセンサーから数分置きにデータを収集し、MQTTでAWS IoT Coreエンドポイントへ送信します。全ての生データはルールアクションによりAmazon Kinesis Data Firehoseストリームへ送られ、Amazon S3バケットへ保存されます。
  2. 毎日お昼休みになるとAmazon EventBridge SchedulerによりAWS Step Functions workflowが実行されます。
  3. ワークフローの最初のLambda関数はDBからその日作業している従業員について推論に必要な情報を取得します。
  4. 2番目のLambda関数は従業員の作業場所の天気をインターネットから取得します。使えそうなAPIがある場合はLambda関数ではなくStep Functionsから直接取得してもよいかもしれません。
  5. Amazon Athena使ってS3バケットから従業員や車のデータを取得します。このとき、先の学習データと同じように9時~10時のデータの中央値を取得します。
  6. Amazon SageMakerエンドポイントにリクエストを行い、各従業員が午後に熱中症になる確率を取得します。
  7. 熱中症になる確率が80%以上の従業員がいた場合、Amazon SNSへメッセージを発行します。
  8. トピックをサブスクライブしているチームリーダーなどの従業員がAmazon SNSから熱中症予防の警告メールを受け取ることで、危険に備えることができます。

構築してみる

機械学習モデルとサーバーレスエンドポイントはここまでで作成して来たものを使用します。
センサーは用意せず、AWS IoTのMQTTテストクライアントを使用します。
Step Functions workflowを起動するEventBridge Schedulerは記事執筆時点でL1コンストラクトしかないのでコンソールから作成します。
DBには本当は関係データベースを使用すべきですが、構築の手軽さを優先して今回はDynamoDBを使用します。推論に必要な一部データをもつinformationテーブルをひとつだけ作り、次のアイテムを入れておきます。本来複数の別々のテーブルに持つべきデータをひとつのテーブルに持っているので、アイテムを識別するためだけのid列を主キーとします。

id work_id work_date work_location user_id birth_date sleeping_hours has_breakfast 10_operation 11_operation 12_operation 10_hydration 11_hydration 12_hydration
1 work_01 2023-12-10 C県D市 user_01 1993-01-01 8 True A B B True False True
2 work_02 2024-01-01 A県B市 user_01 1993-01-01 6 False B B B False False False
3 work_03 2024-02-01 E県F市 user_01 1993-01-01 8 True B B C False False True
4 work_04 2023-12-10 A県B市 user_02 1988-01-01 6 True B B B False True False

各列のデータ型は次の通りです。

res = client.put_item(
  TableName="information",
  Item={
    "id": { "N": "1" },
    "work_id": { "S": "work_01" },
    "work_date": { "S": "2023-12-10" },
    "work_location": { "S": "C県D市" },
    "user_id": { "S": "user_01" },
    "birth_date": { "S": "1993-01-01" },
    "sleeping_hours": { "N": "8" },
    "has_breakfast": { "BOOL": True },
    "10_operation": { "S": "A" },
    "11_operation": { "S": "B" },
    "12_operation": { "S": "B" },
    "10_hydration": { "BOOL": True },
    "11_hydration": { "BOOL": False },
    "12_hydration": { "BOOL": True },
  }
)

以下のスクリプトでアイテムを入れました。

put_item_into_dynamodb_information_table.py · GitHub

CDKのコード

最初にセンサーからデータを集める部分を見ていきます。
SensorDataCollectionコンストラクトとして作成しました。

IoTルールやFirehoseのコンストラクトはまだアルファ版なのでnpmで必要なパッケージをインストールします。

npm install @aws-cdk/aws-iot-alpha
npm install @aws-cdk/aws-iot-actions-alpha
npm install @aws-cdk/aws-kinesisfirehose-alpha
npm install @aws-cdk/aws-kinesisfirehose-destinations-alpha

athenaのデータベースとテーブルはクエリエディタから作成します。

次は推論を行うStep Functions workflowを見ていきます。
特に変わった点は無く、最後のSNSトピック部分はChoiceステートで$.is_alertが真の場合にパブリッシュします。

コンソールのグラフは次のようになります。

ステートマシンのグラフ

最後はSensorDataCollectionコンストラクトとStep Functionsワークフローを含むスタックです。

各Lambda関数のコード

各Lambda関数のコードは以下の様になります。
本来であれば関係データベースにデータを格納し、SQLでクエリするところですが、今回はDynamoDBなので大雑把に全量スキャンします。結果として取得するデータは変わりません。

次にDBから取得したデータをもとに予測に必要な情報をシステム外部から取得します。
今回は天気だけ、それも固定値を返すだけに留めます。

次にS3に格納されたデータをAthenaを使って取得します。9時~10時の中央値などの集計はクエリで行います。

ここまで集めたデータをもとに、Lambda関数でSageMakerエンドポイントへリクエストを行い、熱中症になる確率を予測します。
ひとつのリクエストで全てのレコードの予測が行えます。結果は改行されてそれぞれの予測が得られます。
熱中症になる確率が80%以上となったレコードはユーザーIDと確率をメール本文に記載して通知します。

最後に、cdkコマンドでdeployして完了です。

$ cdk deploy --profile <profile>

動作確認

ここまで作成したモデルやインフラ、プログラムを使用して動作確認を行ってみます。

DBのデータとセンサーからの入力値

DBの内容は前述したものと同様です。各レコードについて少し説明します。

id work_id work_date work_location user_id birth_date sleeping_hours has_breakfast 10_operation 11_operation 12_operation 10_hydration 11_hydration 12_hydration
1 work_01 2023-12-10 C県D市 user_01 1993-01-01 8 True A B B True False True
2 work_02 2024-01-01 A県B市 user_01 1993-01-01 6 False B B B False False False
3 work_03 2024-02-01 E県F市 user_01 1993-01-01 8 True B B C False False True
4 work_04 2023-12-10 A県B市 user_02 1988-01-01 6 True B B B False True False

全部で4つのレコードがあり、予測に必要なデータを持っています。
id1~3はuser_01、id4はuser_02のデータです。
動作確認を行っている今日は2023年12月10日なので、処理対象となるレコードはwork_dateが同日となるid1, 4になります。

センサーから送られてくるMQTTのトピックはheatstroke-topicとします。
データは次のようなものを作りました。1データずつjsonになっています。

9時~10時のデータのみ使用されるため、1番目と7番目のデータは使用されません。
予測のためにLambda関数内で9~10時、10時~11時、11時~12時それぞれの中央値が算出されることになります。

MQTTテストクライアントによるデータ送信

では早速MQTTテストクライアントからセンサーのデータを送ってみます。
10データを個別に送信でも、全てのデータを一度に送信でもどちらでも構いません。Firehoseでバッファされます。

MQTTテストクライアントはAWS IoTコンソールにあります。
heatstroke-topicトピックとしてデータを送信します。

MQTTテストクライアント

Firehoseのバッファを待つため1分少し待ってからS3を確認すると、データが格納されています。

ファイルをダウンロードして内容を確認すると以下データが含まれていることが分かります。

EventBridge Schedulerから実行

お昼の12時になったと仮定して、EventBridge Schedulerから実行してみます。
SNSトピックには私のメールアドレスでサブスクリプションを登録してあります。

今回はスケジュール実行ではなく単発で実行します。

EventBridge Scheduler

時間になり、Step Functionsのステートマシンが実行され、正常終了したことが確認できます。

ステートマシン実行結果

熱中症になる確率が80%を越えたユーザーがいたため、メールが届きました。

受信した熱中症になる確率を記したメール

user_01が84.89%の確率で熱中症になるようです。
このメールを受けてuser_01の午後の作業をどうするのか対応を行うことができますね。

ちなみにuser_02の予測結果はどうだったのか、Lambda関数のログから確認してみます。

PredictLambdaの実行ログ

ユーザーIDも一緒にログに出せばよかったですが、user_01が84.89%でyesだったので、他方がuser_02のものだとわかります。
user_02は87.39%でnoでした。user_02は安心して午後も作業を続けられますね。

まとめ

SageMaker Canvasと今回のシステムについて

今回はSageMaker Canvasを使用して機械学習モデルを作成し、熱中症予防システムを作って実際に動かしてみました。
SageMaker Canvasはとても手軽に機械学習モデルを作成することができ本当に便利で驚きました。今回はレコード毎に独立したデータを学習させてモデルを作成しましたが、時系列データや画像を学習データとしてモデルを作成することもできます。この手軽さはとても可能性に溢れていて、様々な問題に対応するシステムを素早く構築することができそうです。そうして一端PoCとして作ってみて手応えがあれば、今度は機械学習の専門家をアサインして本格的にシステム開発を行うことができそうです。
また、今回改めてAWSの素晴らしさを実感しました。アイデアを思いついてから具体的なアーキテクチャの構想、実際のシステム化までスピード感をもって一気に進めることができました。これは多種多様なサービスが存在することに加え、それらの多くがサーバーレスという選択肢を持っているからこそ実現が可能でした。料金体系として従量課金制という点も大きな要因です。
今回この記事を書いたことにより、今後私が何かを考える際に機械学習モデルの使用が選択肢のひとつとして増えたことがとても嬉しいです。

全体的なまとめは以上ですが、以下に記事執筆中に思ったことを少し書いておこうと思います。

平時のデータの重要性

この記事のテーマを熱中症に決める前、何を予測するモデルを作成するか決めることが非常に難しい問題でした。
世の中には沢山のオープンデータがありますが、学習データに対して何が予測可能で何が予測不可能なのかが私には分からなかったからです。
警察庁が公開している交通事故統計情報のオープンデータから、事故の予測ができないかと思ったところで、事故が起こっていない平時のデータが必要であると気付きました。
しかし、平時のデータを含むオープンデータはなかなか見つけることができませんでした。
何か通常と異なることを予測するための学習データとして平時のデータは必須ですが、使えるかどうかも分からないデータはなかなか保存されないものです。

特徴量エンジニアリングの知識があるとよさそう

SageMaker Canvasは自動で最適なアルゴリズムを選択して機械学習モデルを作成してくれますが、当然学習データが学習しやすいデータである方が精度が上がりそうです。なので特徴量エンジニアリングを少し学んでからSageMaker Canvasを使用するとよいかもしれません。

運用について

今回は機械学習モデルの運用について触れませんでしたが、考慮点として継続的にモデルをアップデートするモデル構築パイプラインは必要になると思います。
増え続ける生データの保存方法も考慮の余地があります。

リアルタイム推論

今回は熱中症予防について考えてみましたが、取り組んでいる問題によってはリアルタイムでの予測が必要となるかもしれません。
IoTデバイスなどエッジでの機械学習モデル活用にはAWS IoT Greengrassを使用すると良いようです。初めて聞いたため、今後調べていきたいです。

aws.amazon.com

SageMaker Canvasの料金について

最後になりますが、料金について少し書いておこうと思います。
SageMaker Canvasには2カ月間の無料利用枠があり、カスタム表形式モデルの作成は1カ月あたり10件が無料になります。
それ以降はトレーニングデータのセル数によって課金されます。セル数はデータセット列数 * 行数です。
最初の1,000万セルまでは100万セルあたり30USDです。

aws.amazon.com

今回記事執筆のためにSageMaker Canvasでモデルを何度も作成し、実験を繰り返しました。楽しかったです。
最終的に作成した熱中症予防のためのデータセットは27列10000行なので、270000セルになります。

最終的に作成したデータセット

参考までに現時点での請求書を載せておきます。

自分のアイデアを形にしたかったので多少の出費は覚悟していましたが、なかなかの出費になりました。
もちろん請求アラームがちゃんと機能していてメールが何度か送られてきましたが、遊んでみる際には注意が必要ですね。

参考