hiroportation

ITの話だったり、音楽の話、便利なガジェットの話題などを発信しています

GCPマイクロサービス・ソリューションアーキテクチャ・ディクショナリー


0. はじめに

GCPベースでマイクロサービス・ソリューションアーキテクトに必要なナレッジをまとめてみました。
本誌は随時更新し拡充・最新化していきたいと思います。

あくまで自身の考えをまとめたものになり、間違えや一部主観も含まれる可能性があるためご了承ください。


1. GCPベースでのマイクロサービス全体設計


マイクロサービス基盤においてGCPを選定する理由・観点
  • 社内、事業部等において、GCPの利用実績やナレッジが豊富であるか?
  • 足りない機能はAWS等、他クラウドを利用する事が可能である(マルチクラウド)?
  • GCPの特定サービスについて利用要求があるか?

※経験上、重要度の高い観点をピックアップしています


マイクロサービスアーキテクチャとはどういうものか

マイクロサービスアーキテクチャは膨大なコンポーネントを整理するために、マイクロサービス毎に「役割」を定義することがとても大切なこととなる。

  • BFFはUX/UIに依存したバックエンド処理を行うため再利用しない
  • ビジネスロジックについて処理を集約するために再利用を前提とした作りとし、ロジックの重複を防ぐことで生産効率化を目指す
  • 外部システムとの連携の際には、専用の連携マイクロサービスを設けることにより、処理や修正対応の集約を実現することができます

GCPではこれら役割を持つマイクロサービスをGKEやCloudRunなどのコンテナエンジンにて実装します。

これにより、コンテナ内の各ロジックを重複なく集約し、無駄のないシンプルなマイクロサービス基盤を実現する。


マイクロサービスの設計観点
論点 説明
サービス粒度・分割単位 業務視点、システム視点、双方での考慮が必要
・単一責任を意識し、必ず複数組織が一つのサービスの意思決定をしないようにする
トランザクション境界、障害時の影響、技術特異性を考慮
俊敏なリリース 後方互換性バージョンアップであり、複数バージョン共存可能なAPIルーティングにより、マイクロサービス毎に独立的なリリースを実現
データ分離 DBなどで扱うデータは整合性のため、単一サービスからの利用とし、複数組織・複数サービスでの利用しないこと
疎結合 マイクロサービスは独立性を持つことで、他プロダクトに依存せず迅速にリリースを実行できる


マイクロサービス設計で意識すること
  • マイクロサービスは拡張に伴いコンポーネントや通信箇所が増え複雑になりやすいため、シンプルな設計を常に心がけること
  • 目先の設計だけで無く、将来を見据えた設計を行うこと
  • 業務ドメインを正確に把握して、ドメインにマッチしたインフラデプロイを心がけること(例えば高セキュア用の基盤など)


GCPにおけるマイクロサービス実装パターンについて

GCPにおいてマイクロサービスを実装するにはいくつかパターンがあり、主に規模や体制面を考慮して選定する。

サービス構成パターン メリデメ
CloudRunを中心に、VPCにて中小規模なコンテナ通信を管理する 主にフルマネージドサービスを利用するため、インフラ管理・運用の必要がないが、スケールに伴い利用サービス数が増え管理が増加するため中小規模での利用が前提となる
GKEを中心に、ServiceMeshを利用した大規模なコンテナ通信を管理する メッシュによる細かいNW制御が可能になるが、GKEクラスタの運用が必要となる
CloudRunとCloud Service Meshを中心とした大規模なコンテナ通信を管理する 上記二つのデメリットを解消できるためほぼ全ての要件でベスプラとなり得るが、メッシュが2024/9現在ベータ版であり、本格利用は少し先


マイクロサービスにおけるシングルテナントとマルチテナント

シングルテナントかマルチテナントかで、マイクロサービス開発のアプローチや難易度が大きく異なってきます。

特に複数ベンダーが開発を行うマルチテナント下では共有ライブラリやマイクロサービスによる共通化は通常の開発と違い、相応の性能試験や利用マニュアルの展開などがあるため、共通化専用チームは必須となります。


どこまで共通化・標準化するのか?

マイクロサービスでは様々な様々なものを標準化

通化項目 通化内容
マイクロサービス 機能の再利用
共有ライブラリ アーティファクト(機能)の再利用
開発標準 コーディングルール(コード規約)


GCPにおけるポリシーコントロール

GCPにおいてポリシー管理方法は大きく2パターンあると考えます。

  • GKE Policy Controllerで管理
  • GCP プロジェクトで管理

マイクロサービスにおいてこれまではGKEをメインで使う事が多かったためPolicy Controller が有益と考えてましたが、 Cloud Service Meshによるメッシュの低レイヤ化により、ポリシーはプロジェクトにて容易に管理可能となる想定。


GCPにおける標準ライブラリの実現について

GCP基盤にておいて標準ライブラリ実装を実現させる場合、Artifact Registryを利用します。 Githubを使っている場合はGithub Packagesを利用しますが、対応フレームワークがネックとなることが多い印象です。

Artifact Registryの場合、GoモジュールやPythonパッケージも保存可能なため、様々な要件に対応する必要があるマイクロサービスと相性が良いように思えます。


2. UX/UI (Webアプリ、モバイルアプリ)

UX/UIには大きく分けてWebアプリとモバイルアプリ(ネイティブアプリ)の2種類が存在する。


2.1. Webアプリ


UX/UIのビジネスロジックをバックエンドに寄せる必要性

一般的にフロントエンドはビジネスロジックをバックエンドに寄せることにより、描画処理に専念し、Light Weightなものとする。 特にSPAを利用する場合、効果を最大限にするためにページ遷移のたびに処理が重くならないようにしたい。


SPAか?SSRか?

UXUIのレンダリングに関しては主にSPAとSSRのどちらかで議論になる。 大規模・高負荷になると、SSRではバックエンドコストが課題なってしまうため、大抵の場合SPAを選定する方が良い印象。 SPAの場合初回表示速度がネックとなるため、SEO対策を実施する事。 初回アクセス時の高速なレスポンスなどSSRがどうしても必要な要件の場合のみ実装する。


マイクロフロントエンドの採用有無

マイクロフロントエンドは1画面にさまざまな情報を持たせる際に適しています。ただし全体的な画面フレームを複数組織で合わせる必要があるため、コミュケーションに注意が必要です。
UXUI全体デザイン設計を行うチームがいれば問題ありませんが、チームを設けることが難しい場合、一般的なSPAを採用します。


SPA(CSR)とSSR等の複数レンダリング対応はなるべく避ける

理想としてはSPAとSSRレンダリングを使い分けられると良いですが、インフラ運用コストが倍以上となるため、なるべく避ける事


CMSを活用して資材を資材の共通化

フロントエンド開発の効率化のため、フロントエンド資材の共通化にはCMSを利用します。 有名どころではAEMでヘッドレスCMSを利用する事によりフロントエンド資材の共通化、再利用により、マイクロサービス全体の開発効率をなるべく効率化される事


2.2. モバイルアプリ


モバイルアプリ開発にて考慮するべき点

モバイルアプリ開発において最初に考慮する点についてまとめる

項目 説明
マルチプラットフォーム モバイルアプリは特にiOSAndroidなどの複数プラットフォームで展開することが一般的なため、コスト低減のためマルチプラットフォームに対応した言語を選定します。2024年時点ではFlutter(Dart言語)がメジャーです。
mBaaS モバイルアプリでは専用のバックエンドサービスが存在しており、それらを使うのが一般的です。特にこだわりがなければシェアの大きいFirebaseを利用することでバックエンド開発コストを大きく削減できます。
iOS開発 モバイルアプリの場合、iOS向けアプリ開発MacOSが必要になるため注意が必要です


マイクロサービスにおける mBaaS の立ち位置

モバイルアプリはできるだけmBaaSを利用してバックエンドをデプロイしつつ、必要に応じてマイクロサービスを再利用したりすることにより、コスト最適化を実現します。


そもそもネイティブアプリ開発コスト削減のためのPWA機能

ネイティブアプリの開発にはWebアプリとは別の専門知識や専用の環境が必要になります。 そのためPWAというWebアプリ機能を使用して、プッシュ通知やオフライン利用といったネイティブアプリのようなことを低コストで実現できます。


3. API


BFFの設計
  • Backend for Frontend の通りフロントエンド設計・開発のために設けます
  • 1フロントにつき1BFFとすることでプロダクト毎の固有ロジックの変更を他プロダクトに依存せず可能


共通なビジネスロジック(マイクロサービス)
  • 通常プロダクトから共通化できそうなロジックを検討して切り出し、共通利用するマイクロサービスとしてデプロイする
  • 共通に利用するマイクロサービスはBFFなどと比べ利用率が高くなると思われるためパフォーマンスや公開方法については特に注意が必要
  • APIの公開はAPI-Gateway(又はApigee)や、GithubPagesとSwaggerを組み合わせて行います


API Gateway
  • GCPにおいては Apigee と API Gateway が主な選択肢となります
  • いずれもAPIアクセスにより料金が発生するため、たとえば認証が必要のないプロダクトに関してはXLB(ExternalLB)から直接プロダクトのバックエンドにアクセスさせるパターンも有りです(流量や不正アクセスへの対応は必要)
  • API Gateway はバックエンドへの入り口となるため、認可制御にも利用します
  • バックエンドへの通信が集中するところであるため、流量制限やリトライ、タイムアウトの役割として利用するのに適しています


4. 認証/認可


マイクロサービスにおける認証方式

複数のサービスを持つマイクロサービスではOAuthやOIDCを組み合わせて、SSOを実現することが一般的。 また、サービスの機密度などによりワンタイムパスワードやMFAなどを追加して適切なセキュリティを設定します。


認証で意識するべきこと
  • フロント(ブラウザ等)に見せて良い情報と見せてはいけない情報を明確に区別すること
  • フロントに高機密な情報(顧客情報など)を持ってくる際には必ずトークン化させたり、シークレット化させたりすること
  • フロントに
  • 認証と認可の役割のコンポーネント


アクセストークンとリフレッシュトークンはフロントに渡す頻度が違う

OIDC標準に従うとアクセストークンとリフレッシュトークンは共にサービスを利用するために使う大切な情報ですが有効期限が大きく違います。アクセストークンは大抵1時間いないですが、リフレッシュトークンは90日以内が一般的です。 この場合、アクセストークンは漏れてもすぐに使えなくなりますが、リフレッシュトークンは長い間不正利用されてしまいます。

なのでリフレッシュトークンはなるべくフロントに渡さないようにする必要があります。


認証認可

マイクロサービスにおける認証設計で大事なことは認証と認可の役割を明確に分けて考えることです。
認証はGoogle Cloud Identity Platform(IdP)を利用して正常に認証完了したアクセスに対してJWTを発行し、JWTを持ち回ってAPI-Gatewayにてアクセスコントロールを行うことでSSOを実現させます。
この時、Cloud IdPは「認証」の役割、API-Gatewayは「認可」の役割を持つことで認証認可の役割を明確にする。


JWTに持たせる情報

Cookieとしてフロントに返却するJWTは認証に使用するため、JWTに持たせる情報を整理することは情報漏洩や不正アクセスにもつながることもあるためとても大事なことです。 例としては以下となります。

JWT設定の注意点 対応策
ユーザを直接特定できる情報は不正利用される可能性があるため入れない UUID+Timestamp等にして時間毎に変化するものとする
JWTの有効期限は必要最小限とする 通常はアクセストークン、IDトークンは30分から1時間、リフレッシュトークンは半年などだが、金融などセキュアなサービスにおいてはより短い時間設定を検討する
OIDC標準に基づいた対策を実施する stateやnonceによる脆弱性対策は必須とし、必要に応じてホワイトリストなども検討する


5. インフラアーキ


GCP マイクロサービス基盤において主に使われるサービス群
主なサービス名 説明
Cloud Armor DDoS対策、セキュリティルール設定に利用
Cloud NAT Podなどの内部からインターネットへ通信するために利用
Cloud LoadBalancer 一般的にはAPI-Gatewayなどの外部からの通信を負荷分散するExternalLBと、ExternalLBから各マイクロサービスへ通信を負荷分散するInternalLBとして利用する場合が多い
Cloud DNS 一般的にインターネットからアクセスできる外部DNSと内部アクセスの内部DNSの2種類をデプロイします
Cloud Identity Platform 認証アカウントの管理に利用
GKE & ASM (次期Cloud Service Mesh) 大規模マイクロサービス基盤のデプロイにはGKE&ASMを利用します
Apigee or API Gateway 大規模であればApigeeを選定それ以外であればAPI Gatewayを選定


VPCの設定
  • DR を想定する場合はリージョン毎のVPCを用意
  • 大規模になれば複数プロジェクトも考えられるためその場合は適宜Shared VPCにてネットワークを結合(集約)する


サブネットの設定
  • 一般的にはマイクロサービスのドメイン単位(Shop、Finance、Commonなど)で設定します


プロジェクトの設定
  • 本番、ステージング、開発環境など環境ごとに作成することが一般的です
  • プロジェクトはAWSでいうとアカウントに当たるため、基盤ポリシー、利用請求毎に作成する
  • 本番環境のみ高セキュアなポリシーのプロジェクトに配置し、その他開発、検証環境等は低ポリシーなプロジェクトに設定


6. IaC

※ IaCツールには一般的にTerraform を使うことが多いため、Terraformベースで記載する


Terraformを使う上での基本方針

Terraformを使う際には以下のような方針で開発を行う

# 基本方針 説明
1 エディタはVSCode 無料、他言語でも利用、多機能のため
2 terraform fmtを必ず使う VSCodeで常に統一した記載を心がけましょう
3 静的解析ツールにはtfsec
4 専用テスト環境を用意しましょう Terraformを商用利用する際は必ずテスト環境で動作確認してからリリースしましょう
5 Providerバージョンは固定させましょう 利用するクラウドのProviderやその他ミドルウェアなどのバージョンは互換性問題を防ぐため固定させましょう


Terraformのリポジトリは目的毎に分ける

マイクロサービス自体もそうですが、Terraformは目的毎に分けましょう。大規模になると、それぞれのサービス毎やインフラ毎にチームを作り、それぞれ独立して開発を進めるため、リリース影響範囲等を明確に分割しましょう。

アンチパターンとして、NWとFW、LB、GWなどのインフラ系を全て同じTerraform又はリポジトリ上に書くのは影響や管理面の問題で避けた方が良いでしょう。


7. データストア・データベース


データストアとデータベースの使い分け

データを格納する際はファイル、データ(データストア)のいずれかの形式で格納するのかを検討する必要があります。 ファイル転送などで利用するのであればファイル形式でも構いませんが、必要なければ基本はデータ形式で格納した方が高速かつ無駄な処理を回避できます。

そのため、以下のようにまとめられます。

サービス 選定観点
GCS ファイルにてデータ転送が必要な場合に利用
Firestore 他データとの整合性を必要としないデータ保管且つフルマネージドでもありシンプルなNoSQLのためこちらを選定する
CloudSQL 複数テーブルのデータとして保管が必要且つの整合性利用
Memcache JWT内UUID値など軽量な情報を保管する場合に利用
SecretManager 鍵情報やパスワードなど漏洩を防止したい内容が含まれている場合に利用
  • 鍵情報やパスワードなど漏洩を防止したい内容が含まれている場合
    • SecretManagerを選定
  • 鍵情報やパスワードなど漏洩を防止したい内容が含まれていない場合
    • ファイルにてデータ転送が必要な場合
      • GCSを選定
    • データとして保管が必要な場合
      • データの整合性が必要なく低コストを優先したい場合
        • Firestoreを選定
      • 複数テーブルが必要でデータの整合性が必要である場合
        • コスト優先ならCloudSQLを選定
        • パフォーマンス優先ならAlloyDBを選定
        • 複数リージョンでの整合性を必要とする場合
          • Spannerを選定
    • リアルタイムで高速書込読込が必要で場合
      • Memcacheを選定


Firestoreのモード使い分け

Firestore にはネイティブモードとDatastoreモードの2種類あるが、ネイティブモードはリアルタイムアップデート機能によりモバイルアプリと相性が良い。Datastoreモードは通常のWebアプリなどで利用する。
ただし、これらのモードはプロジェクト毎にどちらかの設定であるため慎重に選択すること。変更する場合はストアの情報を初期化する必要がある。


マイクロサービスにおけるデータストア・データベース利用の注意点
  • データストアやデータベースへのアクセスは必ず1つのマイクロサービスで行うことで複数組織による責任を明確にすることによって、デグレデッドロックを防ぎ結果整合性を実現します
  • 利用用途によって適したDBを選定すること
  • 機密性の高い情報を扱う際には公開範囲の設定を検討すること
  • データ保持期間(TTL)を明確にすること


8. インフラセキュリティ


マイクロサービスにおけるデータ保護

マイクロサービス基盤にてデータ保護のために工夫することは以下になります。

  • セキュアな情報はSecretManagerを必ず使い、利用回数を最低限とする
  • JWTなどのフロントにセキュアな情報を出さないようにバックエンドを工夫する
  • データ保護はフルマネージドサービスを中心に利用することにより、無駄なセキュリティリスクを抑制します


コンテナイメージを考える

セキュリティ観点でコンテナベースイメージには以下のような対策を実行します。

  • ベースイメージにはオフィシャルを利用し、それ以外のイメージは利用しない
  • Alpineを選定し、内包ライブラリを最低限にする
  • Dependabotなどで依存関係にあるパッケージの脆弱性検知を行う
  • Artifact Registryによるコンテナ脆弱性スキャン機能の利用

高速ビルドを意識する場合は主に以下を考慮します。

  • レイヤー キャッシュを意識したDockerfileを作成する
  • dockerコマンドのビルドキャッシュオプションを利用する


Cloud Service Meshのセキュリティ
  • GCPセキュリティに必要なベストプラクティス以下になります

cloud.google.com


9. 監視・モニタリング


監視のイメージ

特に大規模なマイクロサービスの場合、膨大なコンテナのログから状況を把握することは重要となる。そのため一般的には統合監視可能なツールを取り入れ、管理を容易にします。

  • GCPの場合様々な指標の情報を統合的にモニタリングする場合はCloud Monitoringを利用
  • メッシュ内の通信経路、速度、ステータスに関してはServiceMeshのダッシュボード(Anthosなど)
  • マイクロサービスにおけるフロントから各コンテナのトレース情報は Cloud Trace


APIリクエストをどのようにトレースするのか(トレーサビリティ)

Cloud Service Mesh であれば Cloud Trace を利用して顧客リクエストをトレースデータをモニタリングできます。

https://cloud.google.com/service-mesh/docs/observability/accessing-traces?hl=ja#managed-td


10. レジリエンス


トラフィックへの対応


マシントラブルへの対応
  • コンテナによる冗長構成
  • ゾーン冗長
  • サーキットブレーカー


自然災害対応について
  • リージョン冗長は基盤を倍持つ必要があるため、大規模基盤や重要基盤にのみで検討する
  • リージョン冗長のためにAct-Act を検討することにより、高トラフィックによるレジリエンスを向上させられる
マイクロサービスにおけるリトライ処理注意点
  • マイクロサービスでは1リクエストにつき、複数コンテナでリトライ処理が考えられますが、その場合無邪気に全てのコンテナでリトライをしてしまうと、アクセス負荷が爆増してしまいます
  • 1リクエスト毎の最大リトライ回数を設定するため、API-Gatewayでリトライを設定する


11. テスト


モックツール

単体テスト結合テストなど対向先との連携テストのためモックツールを利用します

  • WireMock
  • Mockoon


負荷テストツール


Pact
  • 結合テストにおいてPactにてサービス間テストを行うことで、各サービスが提供するAPIの入出力の仕様を契約として明確に定義し、サービス間の連携が正しく行われているかを確認できます。


12. パフォーマンスチューニング


主なパフォーマンス改善例
  • フロントキャッシュ(storeなど)による情報取得時間改善
  • 通信シーケンス見直し・削減による改善
  • KeepAliveによる通信改善
  • 性能チューニングによるパフォーマンス改善


13. DevOps


GCPにてGithub Actionsの利用について

Github Actionsにてパイプラインを起動する際、サービスアカウントではなくWorkload identityを利用することにより、ID等の漏洩を防ぐことができる。その他GCPとの外部連携にはWorkload Identity を利用することがセキュリティベストプラクティス。


リリースパターン

マイクロサービスにおいて主に以下リリースパターンを実装することにより、俊敏で安全なリリースを目指します。


DBにおける後方互換性について

RDBにおいてたとえばカラムの設定変更等を行ってしまうと、それまでカラムを利用していた別バージョンのマイクロサービスに影響が出てしまいます。そのためRDB後方互換性を持たせたリリースを行うには、更新ルールを設ける必要性があります。

  • DBのカラムやインデックスなど設定を更新する場合、更新はせず、追加で対応すること
  • 基本削除設定は行わないこと
  • RDBの更新や削除は参照しているマイクロサービス全バージョンに影響が出ないことを確認した上で行うこと

これらのルールが守れない場合は基本的にはRDBを使わずKeyValue のNoSQLを利用します。


14. よくあるマイクロサービスアーキテクチャでの検討


特定のトラフィックだけを通すアーキテクチャパターン

コストをかければVPNなど検討できますが、不特定多数のアクセスを想定する場合は Cloud Load Balancer(XLB)にてクライアント証明書を設定します。 それにより証明書をインストール済みの端末のみ環境にアクセスが可能になります。 自己認証局をデプロイすれば証明書自体はフリーで作成可能のため、低コストで実現が可能となります。


非同期処理について

非同期処理のためゼロスケーリング可能なCloudRunやFunctionsなどを利用することでコストを最適化できる

利用サービス名 説明
Cloud Scheduler 非同期にタスクを実行する場合は基本スケジューラを利用する
Cloud Workflows Workflowsを使わない設計パターンも考えられるが、複数の処理実行の場合は1処理毎で分割することにより、テストの容易化やデバッグの容易化のため、導入はベストプラクティスとなる。またWorkflowsにてタイムアウトやリトライなどの制御が可能
Cloud Run Cloud Functionsを使うこともできるが、専用パイプラインを作る必要がある。Cloud Runであればジョブ実行も可能のため、選定サービスを不用意に増やす必要がなくなる。


GCSからのファイル転送

GCSから特定箇所にファイル転送する方法はいくつか方法がありますが、

利用サービス名 説明
Cloud Run Cloud Runのジョブを利用してgcloud storageコマンドを実行することにより容易にファイルを転送できます。別途設計パターンは考えられますが、一番低コストで実現できます


GCSの設計イメージ

GCSにファイルを格納する場合、バケットにて分けて格納するか、フォルダに分けて格納するかの2パターンが考えられます。複数サービス・プロダクトが乗るがマイクロサービス基盤では、1プロダクト毎にバケットを分ける方がアクセス権限がシンプルに分割できるため利用しやすいと言えます。

シドニーマラソン 2022走ってきました!

はじめに

突然ですがシドニーラソン走ってきました。
仕事柄全然体を動かさないため、体力作り、ストレスコントロールのため始めたランニングから10年ほど経ちましたが、 ついにここまできました。。

最初は500mもキツかったですが継続すれば意外と何とかなるものですね。。

シドニーラソン2022について

20周年と3年振りの開催もあって、とても盛り上がってました。
シドニーラソンは海外マラソンとしてはサポートがしっかりしているらしく、 申し込みから当日までの細かな説明や案内で迷うことはありませんでした。

ラソンコースもとてもきれいなので、初の海外マラソンとしてオススメされているようです(走った後に知ったw)。

また当日は高橋尚子さんもいらしていました。

ラソン前日

写真撮りまくりました。 まずはオペラハウス。
ラソン前日当日後日で天気は良い感じで晴れていました。

ゼッケン引き換え

ディナー時の花火

ラソン当日

当日の朝はシドニーラソンのため電車が無料なようです。 そのためマラソンには財布など現金は一切不要です。
スタート地点には必要最低限の荷物で行けばOKですが預かりサービスもあります。

(マラソン当日の写真は後日貼ります)

私はまだまだマラソン初心者ですが、4時間30分ほどでの完走でした。 次はサブ4目指します!

ラソン後日

帰国日の早朝、軽くウォーキングをしながらシドニーの街並みを撮りました。

歩いていて気づきましたが、タバコのポイ捨てなど全く見つからず、その上ヨーロッパ調の建物と街並みでとてもきれいでした。

帰国時日本は台風のため、カンタス航空は延期してましたが、代わりにANAで予定通り帰れることになりました。

オーストラリア(シドニー)に行ってみて

物価高い問題:
円安もありますが、オーストラリアは物価がものすごく高かったです。
大体日本の3倍はします。水が3ドル以上、ビールが8ドル以上て感じ。

ヨーロッパと比べて:
ドイツはたばこのポイ捨てがすごかったですが、シドニーはポイ捨てを見かけませんでした。 街並みもきれいですし、オーストラリアが好きになりました!

料理について:
全般的に美味しかったですがポテトの量がやばかった。 何かメイン料理を頼むと、必ずフライドポテトがマッシュポテトがでてきました(ドイツと一緒)。
日本で言うお米の代わりなんでしょうね。

気候について:
海外旅行すると毎回思うことですが、日本と比べてとても空気がすっきりしていて湿度がちょうど良い感じです。 逆に日本に帰国すると日本のジメジメ気候にがっかりします。。

PCR検査について:
海外のほとんどはもうPCR検査していません(多分)。 日本は最近帰国時のPCR検査はワクチン3回摂取していれば摂取証明書にて免除されるようになったので、仕事をしている方でも気軽に海外行けるようになったかと思われます。油断は禁物ですが、マスクをしている人もシドニーでは全く見かけませんでした。

ネットワークについて:
コロナ前は海外行くと必ずGlobalWifiを借りていましたが、現代ではAhamoなどの海外ローミングに対応している格安契約で十分な気がします。 3000円以下で海外でも定額プランの通信ができるのは本当にありがたいです。

アジャイル開発の進め方

私が経験したアジャイル開発手法(スクラム)の進め方についてまとめてみました。
基本的に上から読み進めれば、アジャイル開発の流れを理解できるように書いています。
一部自己解釈が含まれている可能性があるためご了承ください。


1. そもそもアジャイル開発とは?

  • 設計は最初からガッチリさせず、小さな単位で開発とテストを繰り返し、少しずつ不明点を解消しながら全体を明確にする感じと思っている
  • アジャイルとは「素早い」という意味ですが、必ずしも最短で開発を完了させる、ということではないです
  • 早い段階で開発フェーズを始められるため、やってみてわかるエラーや不明確部分などの想定外に早期対応でき、大きな手戻りを減らせます
  • 徐々に仕様を固めていくため突如の仕様変更などにも応えやすい


2. 用語解説

用語 説明
スプリントmtg 前スプリント期間での反省や次スプリント期間でのやることなどを決めるためのmtgです。1週間〜2週間の期間を設けることが多いです。
デイリースクラム 進捗状況を確認するためのmtgです。毎日メンバーが集まれる時間に実施します。メンバに遅延が発生していないか、共有事項等はないかなどを確認します。
ストーリー ユーザーストーリーとも呼び、「要求事項」のようなものです。開発者はこの要求事項を元に開発に必要なタスクを決めていきます。
ストーリーポイント ストーリーを解決するための工数で、基準ストーリーから相対的に決めていく。
基準ストーリー ストーリーポイントを決めるために使用する基準で、大抵は単純な作業に設定します。
スクラムマスター スプリントmtgスクラムmtgでの司会を務める人物を指します。
アクティブバックログ スプリント内でやるべきストーリー群のこと。
ベロシティ 開発、作業スピードのこと。


3. アジャイル開発のやり始め

  • まず抽出した要件定義からどれくらいの期間がかかるか予測する
    • プロジェクトに必要なトータルストーリーポイントをあらかじめ予想して算出する(これが大変だと思う)
      • ベロシティを考慮していないため、何人かでポイント数については議論しておいた方が良いと思います
    • トータルストーリーポイントから何スプリントでプロジェクトが完了できるかを見積もる
    • 規模によりますが、2、3スプリントくらいはバッファを積んでおいた方が良いかも
    • このとき、1スプリントのストーリーポイントも算出しておいて、後々のスプリントmtgで遅延予測などに使用しましょう
    • 以下のようなイメージですね(やっぱり図にした方がわかりやすい)

規模を見積り、ベロシティを計測して、期間を予測する - YouTube

  • アジャイル開発のルールを決める
    • チケットシステムは何を使うか?(Jira?Redmine?など)
    • スプリント単位はどうするか?(何日?何週間?)  * スプリント中はmtgで決めた目標に向かってチームで最大限に動く期間とする感じ
      • つまり仕様変更や開発が活発な場合は、定期的なスプリントmtgを増やすためスプリント単位を小さくすると良い
    • スプリントmtgはいつ何時にやるのか?何を話すのか?
    • デイリースクラムは何時にやるか?何を話すのか?
    • チケットには何を書くのか、どう運用するのか?
    • スクラムマスターは誰が担当するのか?
  • 決めたらチーム内でルールについて議論をし合う
  • チケットシステムを準備しておく


4. プロジェクトを進めてみる

日常のプロジェクト内では毎日デイリースクラムで進捗確認をし、一定期間毎にスプリントmtgで開発等に関する反省や次スプリントでやること等を考えます。
全体としては以下のような流れです。

f:id:thelarklife1021:20211201024520p:plain:h300


4.1. スプリントmtg前にやること

  • 要件事項や、やりたいことをにもとにしてストーリーチケットを作成しておく
  • ストーリーチケットには具体的にやることを記載する
    • チケットの内容を抽象的なままで先に進めてしまうと、後々のストーリーポイント決めがブレるし作業遅延の原因になる
    • やることが不明確なタスクは、調査ストーリー又は調査タスクとして扱い、優先的に解決させましょう


4.2. 最初のスプリントmtg

  • 各メンバーはスプリント期間中の稼働時間を提示する
  • 作成したストーリーチケットを元にチームで必要なタスクが十分かを確認する

  • 基準ストーリーを決めておく

    • メンバー全員が理解できる単純な作業が良いと思います
  • ストーリーポイントを決める
    • 1スプリント内で実施できそうな分のストーリーポイントを以下の流れで決めていきます
    • 各メンバーは基準ストーリーから相対的にポイントがいくつになるのかを決める
    • メンバー全員で一斉にポイントを出し合う(やり方は自由)
    • ポイントがバラけた場合は高く設定した人のポイントを採用する
      • ただしポイントの低い人と高い人に大きな開きがある場合はその場でお互いの認識を話し合ってポイントをどっちに寄せるのかを決める
    • 詳細不明なタスクはストーリーポイントが不透明になるため、調査タスクとしておき、ポイント見積りはしないようにする
      • 調査タスクはボリュームが不透明なためなるべく優先させて解決させましょう
      • また、あとで完了した調査タスクはスプリントmtg前までに対応者がおおよそのストーリーポイントを設定しておきましょう
  • ストーリーで優先したいものはアクティブバックログバックログの上部に持っていきましょう
  • 各ストーリーの担当はmtg内で決めても良いですし、各々でやりたいストーリーチケットを決めても良いですが優先順位は意識しましょう


4.3. スプリントmtg後にやること

  • 各メンバーは担当するストーリーチケットのやることを元に子チケットを作成してから作業を開始する
    • なるべくストーリーに書いたやることを、そのまま子チケット名として作成するとわかりやすい
    • 着手するタイミングで考えられる子チケットは全て作成しておき、後付けがなるべく発生しないようにする
    • 該当子チケットを開始するタイミングでステータスを開始に変更する
  • チケットのステータス更新は毎日行うようにする
    • 毎日進捗を可視化できる状態にしておく
    • 1日で終わらなかった子チケット(タスク)は分割して、1日でどれだけ進んだのかをわかるようにする
      • これをしないと進捗が不透明になってしまうので、めんどくさがらずやる習慣をつけましょう


4.4. デイリースクラム

  • 各自のスプリントの進捗を確認し、抱えている問題や、自分が解決したこと等を共有し、必要に応じてスプリントバックログを見直します。
  • デイリースクラムでの話す内容
    • 今日やった事の進捗 *「予定通り」か「遅れているか」を話し合う
      • 逆にそれ以外のことはなるべく当事者同士で話し合い、他の人の時間を奪わないように意識する
      • 遅れている場合はスプリント内に追われそうかを軸に報告する
    • 明日やる事
    • 共有するべきこと
  • スプリント内での解決が難しい場合は作業分担をして、最悪次スプリントに伸ばすかどうかも検討してください


4.5. 2回目以降のスプリントmtg

  • 調査タスクなどのストーリーポイントが更新されていることを確認する
  • 2回目以降は前スプリントの反省をして次スプリントに活かしましょう
    • 設定したストーリーが終わり切らなかったのはなぜか?
      • 追加業務が発生してしまった
      • 想定外のエラーが発生してしまった
      • ストーリーポイント低く設定してしまった
      • など
    • ベロシティが大きく変動したのはなぜか?
      • ベロシティが正確ではないから
      • チームで良い開発手法を活用していたから
      • まだチームで開発し始めたばかりだから
      • など
    • ベロシティを求めてチームのパフォーマンスを理解する
      • スプリント内で完了した合計ストーリーポイントとメンバーの合計稼働からベロシティを求める(=実績)
      • 次に、求めたベロシティと次スプリントの稼働予測から次スプリントにおけるストーリーポイントを決めます(=予測)
      • 計算方法は以下です
実績:pt / bd = pt/bd
予測:pt/bd * bd = pt

pt:合計ストーリーポイント(道のり)
bd:全メンバーの稼働時間(時間(日単位))
pt/bd:ベロシティ(速度)
  • 算出した実績ベロシティ(pt/bd)についてチームで分析し意見を言い合う
    • ベロシティの値が予想より大きい場合に考えられること
      • チームが開発について習熟し始めてる
      • 開発に便利なツール類を導入した
      • ストーリーポイントの付け方が甘い
      • など
    • ベロシティの値が予想より小さい場合に考えられること
      • 具体的にタスクが整理できていない
      • 急遽休むメンバーがいた
      • 開発の仕方に問題がある
        • チームで情報共有ができていない
        • メンバーにやる気がない
      • ストーリーポイントの付け方が厳しい
      • など
    • ベロシティの値が予想値に近いまたは等しい
      • ベロシティが連続で予想値に近似しているとチームの開発速度が実際に見えてきていることになるので良い傾向
      • その状態で次スプリントは、少し多めのベロシティを設定してチーム力を上げていきましょう
  • 算出した実績ベロシティから次スプリントで完了できそうな量のストーリーポイント(pt)の予測を算出します
  • 算出したストーリーポイントから実際にプロジェクトがあらかじめ算出した予測期間が完了できそうかを確認しておきます
    • トータルストーリーポイントから分割したストーリポイントよりもかなり低い場合
      • 開発期間延長を検討
      • メンバーの追加アサインを検討
      • プロジェクト内で開発するコンポーネント等を削る
        • 意外とこれが大事な気がする
        • 完成形を最初から作らず最低限のものを作ることに努める
  • 予想したストーリーポイント分、次スプリントで実施するストーリーとそのポイントを決めていきます
    • やり方は1回目と同じです


5. スクラムマスターがやること

  • スプリントmtg
    • チケットが最新であることの確認
    • スプリントmtgの進行
  • デイリースクラム
    • デイリースクラムの進行
    • 進捗していないタスクはその理由を確認する
    • ある人のタスクを別の人がブロックしていないか確認する
  • あとは経験!


6. アジャイル開発の目的を再認識しましょう

  • 手戻りを徹底的に無くしたい

    • チケットは具体的に書いて、チーム内のコミュニケーションミスを極力無くすように努力する
    • スプリント期間が長くなり過ぎないように気をつける
  • プロジェクトを順調に進めるため、毎スプリントのストーリーは全て完了させたい

    • ベロシティから最適な人数、開発期間を早めに算出するようにする
    • ストーリーチケットのやることは具体的に書くようにし、わからないことは人に聞き、生産性を最大限にあげよう
  • ベロシティは実体と合っているのか

    • ベロシティ値は高ければ良いというわけではなく逆に大きく予測よりも高く達成した場合は、ストーリーポイントの見積もりが甘い可能性がある
    • ベロシティはチームの開発スピードを指すので、チームみんなが正直な気持ちでストーリポイントの算出をすることが大事
    • 前スプリントと比較してベロシティ値を無理に高く設定するのはやめた方が良い
  • プロジェクトゴールに向けて気づいた追加タスクややりたい事はチケット化して忘れない内にバックログに溜めておきましょう


7. Q&A

  • ストーリーポイント決めで高めに設定した人の方を採用する理由は?

    • 低いポイントを採用してしまうと対応するメンバーによってはストーリーを予定期間に解決できない可能性が出てくるから
  • チケットは具体的にどこまで書くのが良いのか?

    • 最善はチームメンバー全員が背景からやることまでを全て具体的にイメージできるようになることです
    • これができていないとストーリーポイントがブレたり、メンバーによっては想定以上にタスク完了までの時間がかかってしまう恐れがあります
    • よく手を抜いて省略してしまうことが多いですが、間違って作業して手戻りが発生するよりは遥かにマシなので、やることは具体的に記載し、わからないところは調査タスクとして分割するようにしましょう


以上

Amazon SageMaker入門

Amazon SageMakerを入門向けにいくつかまとめてみました。
(随時 追加していきたいと思います)


1. SageMaker サービス全体像

f:id:thelarklife1021:20211031011627p:plain:h300

(引用先:https://aws.amazon.com/jp/sagemaker/)

SageMakerには様々な機能が存在しますので、今回は一番入門しやすい部分に絞って触ってみたいと思います。


2. SageMaker Studio を準備

SageMaker を始める際にはまずドメインと呼ばれるを専用環境をVPC内に定義する必要があります。
このドメインの中で学習に必要なインスタンスを立ち上げたり、推論器に使うエンドポイントを使ったりなどをします。

f:id:thelarklife1021:20211031023945p:plain:h300

SageMaker用に使う、アクセスロールやVPC、ノートブック共有先、それとJumpStartを設定します(土台の準備)。
JumpStartは事前構築済みモデルなどを使うことができる機能で、機械学習未経験者はまずこれを触っていくのが良いと思います。

f:id:thelarklife1021:20211031031633p:plain:h300

しばらくするとDomainのステータスがReadyになります。
この状態ですと関連リソースの定義を行なっているだけで、デプロイしているわけではないため、料金は発生しません。

f:id:thelarklife1021:20211031033316p:plain:h300

土台が出来上がったので、利用者を設定していきます。

f:id:thelarklife1021:20211031035255p:plain:h300

ユーザ作成が完了するとユーザ毎にSageMaker Studioが割り当てられます

f:id:thelarklife1021:20211031035407p:plain:h150


3. Jumpstart で物体検知(YOLO)を試す

Studioが立ち上がりましたら、Jumpstartにてモデルを選択します。

f:id:thelarklife1021:20211031120655p:plain:h300

今回は物体検知に使われるYOLOを選択します。
既存のモデルを使うため、このタイミングでEndpointを作成することによって、推論器として利用できます。

f:id:thelarklife1021:20211031121224p:plain:h300

Endpoint Status が「In Service」になりましたら、Notebookを開いて、YOLOモデルを実際に使っていきます。

f:id:thelarklife1021:20211031124106p:plain:h150

立ち上げたNotebookのコードを順に実行してトレーニング・モデルの評価を実施します。
今回のモデルセットでは以下の動きをします。

順序 実行内容
1 Jumpstart用のS3からイメージのダウンロード
2 イメージから物体検知を行い、エンドポイントを作成
3 バウンディングボックスにてモデル予測を可視化

f:id:thelarklife1021:20211031131358p:plain:h300

二つ目まで実行すると、下記のように物体検知した座標とそのラベルが決まる
(対象コードにてShift+Enter)

[[[0.3936842679977417,
   0.631632387638092,
   0.5039596160252889,
   0.9389058947563171],
  [0.15203692515691122,
   0.7994152307510376,
   0.29037004709243774,
   0.9981174468994141],
  [0.28708767890930176,
   0.6139858961105347,
   0.3966217041015625,
(~snip~)
 ['chair',
  'chair',
  'chair',
  'chair',
  'diningtable',
  'chair',
(~snip~)

三つ目まで実行すると以下の通り予測結果(バウンディングボックス)が出力される

f:id:thelarklife1021:20211031175403p:plain


4. Jumpstart で自然言語処理(BERT)を試す

BERTにて自然言語処理を行います。ここでは、文書内容からポジティブかネガティブかを理解し、出力します。
こちらもYOLOと同様の手順で進めていきます。

f:id:thelarklife1021:20211101045443p:plain

BERTでは以下の動きをします。

順序 実行内容
1 boto3とjsonのインポート
2 BERTに入力するテキスト2つを定義
3 BERTを使ったエンドポイントを作成

f:id:thelarklife1021:20211101060146p:plain

3ステップ実行すると以下の通り2つの予測結果が出力されます。
結果からは上部は「ポジティブ」、下部は「ネガティブ」

Inference:
Input text: 'astonishing ... ( frames ) profound ethical and philosophical questions in the form of dazzling pop entertainment'
Model prediction: [-4.22542048, 4.55869722]
Model prediction mapped to labels: positive

Inference:
Input text: 'simply stupid , irrelevant and deeply , truly , bottomlessly cynical '
Model prediction: [3.8848784, -4.23237181]
Model prediction mapped to labels: negative

Google翻訳した結果は以下ですが、何となくあってるかも。

text1:驚くべき...(フレーム)まばゆいばかりのポップエンターテインメントの形での深い倫理的および哲学的質問
text2:単に愚かで、無関係で、深く、本当に、底なしに冷笑的です


5. SageMaker Autopilot 使用した AutoMLによる機械学習を試す

これまでは既存モデルを使用してデータを作成していましたが、ここではAutoMLを使用した機械学習を進めます。
以下公式サンプルを使用します。このデータセットは銀行の電話でのダイレクトマーケティングを行った結果、口座開設まで至れるかをyes or noで予測するものとなっています。

github.com

左のGitタブのから /amazon-sagemaker-examples/autopilot/sagemaker_autopilot_direct_marketing.ipynb をクローンしてきます。

f:id:thelarklife1021:20211101012811p:plain

後は上から一つずつ実行していきます。
実行概要は以下になります。

順序 実行内容
1 sagemakerやboto3といった必要なライブラリをインポート
2 機械学習に使うデータセットのダウンロード
3 S3にデータセットをアップロード
4 SageMaker Autopilot ジョブを設定
5 SageMaker Autopilot ジョブを実行
6 SageMaker Autopilot ジョブの進捗をトラッキング
7 結果

6.の進捗で AnalyzingData -> FeatureEngineering -> ModelTuning -> GeneratingExplainabilityReport -> Completed と変化しており、
特徴量エンジニアリングやハイパーパラメータチューニングなどを自動で行なっています。

この時のジョブに関しては以下コンソールの「処理中」から確認できます。

f:id:thelarklife1021:20211101040200p:plain

今回の学習結果で以下の通り口座開設に至った結果を出力しています。

f:id:thelarklife1021:20211101041937p:plain

出力全体を確認したい場合はS3の inference_result.csv に出力されるのでそこを確認します。


6. データ準備について触ってみる (Data Wrangler)

機械学習用のデータ収集、前処理を行う場合は Data Wrangler を使います。
今回は以下鉄板不良のデータセット(CSV)を使用します。

www.openml.org

データセットをにS3コンソールからアップロードします。

f:id:thelarklife1021:20211101051134p:plain

S3から Data Wrangler にインポート。

f:id:thelarklife1021:20211101051229p:plain

Data flow として表示され、データを変換したり分析したりといったことが可能になります。

f:id:thelarklife1021:20211101055042p:plain

データ分析する場合は以下の通りテーブルやグラフなど様々形で可視化できます。

f:id:thelarklife1021:20211101055430p:plain

データ変換の場合は以下の通りデータフローをUIから設定できる

f:id:thelarklife1021:20211101063348p:plain


7. SageMaker Pipelines を試す

f:id:thelarklife1021:20211101072352p:plain

(https://aws.amazon.com/jp/solutions/implementations/aws-mlops-framework/?nc1=h_ls)

主に以下を使っていきます。

github.com

サービスカタログにはJumpStartを有効化ことでMLOps用のテンプレートが使えるようになります。

今回は以下を使用します。

f:id:thelarklife1021:20211101075015p:plain

プロジェクトを開始することによりCloudFormationによるMLOpsに必要な環境が作成されます。 以下はMLパイプライン

f:id:thelarklife1021:20211101080835p:plain

CodeCommitにはMLOpsに必要なレポジトリが格納されます。

f:id:thelarklife1021:20211101080726p:plain

pipelines/abalone/pipeline.py の transform_instances を適当に編集します。

ml.m5.large -> ml.m5.xlarge

そうすると以下の通り Changedが1になります。この状態でpushを行い、パイプラインを回していきます。

f:id:thelarklife1021:20211101082842p:plain

pushすると下記の通り Codebuild が実行されます。

f:id:thelarklife1021:20211101084320p:plain

SageMaker Pipeline もCodePipelineで実行されていることが確認できます。

f:id:thelarklife1021:20211101084546p:plain

入門編として概要レベルで実践してみました。
次回は詳細にMLOps構築を進めていきます。

以上

CNNをゼロから実装

畳み込みニューラルネットワーク(CNN)の復習です。

画像データの配列

まずはデータを準備します。実際は画像データを前処理するところからやりますが、今回は省略します。 高さh、幅w、チャンネル(色)ch、データの個数nとすると、データの形状は(n, ch, h, w)、となるような画像データをランダムで生成します。

import numpy as np

img = np.random.randint(0, 50, (1, 1, 7, 7)) # 任意の範囲の整数の乱数、最小値0、最大値50
# img = np.round(img)
print(img.shape)

print(img[0].shape)
print(img[0])

ランダムで出力される値を確認する

(1, 1, 7, 7)
(1, 7, 7)
[[[ 4 27  8 19 10 29 40]
  [40 40 24 15 12  8 13]
  [27 41  1  7  1  5 42]
  [ 0 37  5 49 30 31  0]
  [ 7 48 17 14 43 34 37]
  [35 13  8 18 24 45 40]
  [16 29  2  6 12 30 10]]]

次に画像データの持ち方を考えます。

import numpy as np

A = np.array(
    [[["000", "001", "002", "003"],
      ["010", "011", "012", "013"],
      ["020", "021", "022", "023"]],
     [["100", "101", "102", "103"],
      ["110", "111", "112", "113"],
      ["120", "121", "122", "123"]]]
)
print(A)

print(A.shape)
[[['000' '001' '002' '003']
  ['010' '011' '012' '013']
  ['020' '021' '022' '023']]

 [['100' '101' '102' '103']
  ['110' '111' '112' '113']
  ['120' '121' '122' '123']]]
(2, 3, 4)

im2colによる展開

畳み込み演算やプーリング演算を、for文を重ねなくても実装できるように、入力データを展開処理するために使用される関数。

# 引数は
# 画像データ、カーネル高さ、カーネル幅、ストライド幅、ストライド高さ、パディング高さ、パディング幅
# ストライド量、パディング量は縦横まとめられる場合あり
def im2col(img, k_h, k_w, s_h, s_w, p_h, p_w):
    n, c, h, w = img.shape 
    # print(img.shape)

    # パディング処理
    img = np.pad(img, [(0,0), (0,0), (p_h, p_h), (p_w, p_w)], 'constant') 
    # print(img[0])
    # print(img.shape)

    # 出力データのサイズ計算
    out_h = (h + 2*p_h - k_h)//s_h + 1
    out_w = (w + 2*p_w - k_w)//s_w + 1

    col = np.ndarray((n, c, k_h, k_w, out_h, out_w), dtype=img.dtype)  # 戻り値となる4次元配列を準備。(データ数、チャンネル数、カーネル高さ、カーネル幅、出力高さ、出力幅)
    # print(col.shape)
    # print(col[0])
    
    # フィルターに対応する画素をスライス(colに代入)
    for y in range(k_h):
        y_lim = y + s_h * out_h # y_lim:最後のフィルターの位置
        # print("y_lim")
        # print(y_lim)
        for x in range(k_w):
            x_lim = x + s_w * out_w # y_lim:最後のフィルターの位置
            # print("x_lim")
            # print(x_lim)
            col[:, :, y, x, :, :] = img[:, :, y:y_lim:s_h, x:x_lim:s_w] # colのy番目、x番目に、yからy_limまでをストライド量ごとにスライスしたものを代入
            # print("col")
            # print(col)
            # print("img")
            # print(img[:, :, y:y_lim:s_h, x:x_lim:s_w])
    
    # transpose: 多次元配列の軸の順番を入れ替え。reshapeしやすいように、順番を並び替え。(データ数、出力高さ、出力幅、チャンネル数、カーネル高さ、カーネル幅、)
    col = col.transpose(0, 4, 5, 1, 2, 3)

    # reshape: -1を指定することで、多次元配列の要素数を自動整形。(データ数×出力高さ×出力幅 , チャンネル数×カーネル高さ×カーネル幅)
    col = col.reshape(n*out_h*out_w, -1) 
    return col

畳み込み

畳み込みに必要な関数を用意します。

class Convolution:
    def __init__(self, W, b, stride=1, pad=0):
        self.W = W # フィルター(カーネル)
        self.b = b
        self.stride = stride
        self.pad = pad

        # 中間データ
        self.x = None   
        self.col = None
        self.col_W = None

        # 重み・バイアスパラメータの勾配
        self.dW = None
        self.db = None

    def forward(self, x):
        k_n, c, k_h, k_w = self.W.shape # k_n:フィルターの数
        n, c, h, w = x.shape
        
        # 出力データのサイズ計算
        out_h = int((h + 2*self.pad - k_h) / self.stride + 1)
        out_w = int((w + 2*self.pad - k_w) / self.stride + 1)
        
        # 展開
        col = im2col(x, k_h, k_w, self.stride, self.stride, self.pad, self.pad) # 画像を2次元配列化 (データ数×出力高さ×出力幅 , チャンネル数×カーネル高さ×カーネル幅)
        col_W = self.W.reshape(k_n, -1).T # フィルターを2次元配列化
        out = np.dot(col, col_W) + self.b #行列積(畳み込み演算)

        # 整形
        out = out.reshape(n, out_h, out_w, -1).transpose(0, 3, 1, 2) # 2次元配列→4次元配列

        return out

プーリング

画像をMax Poolingしていくための関数を用意します。

class Pooling:
    def __init__(self, pool_h, pool_w, stride=1, pad=0):
        self.pool_h = pool_h
        self.pool_w = pool_w
        self.stride = stride
        self.pad = pad

        self.x = None
        self.arg_max = None

    def forward(self, x):
        N, C, H, W = x.shape
        out_h = int(1 + (H - self.pool_h) / self.stride)
        out_w = int(1 + (W - self.pool_w) / self.stride)

        col = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)
        col = col.reshape(-1, self.pool_h*self.pool_w)

        arg_max = np.argmax(col, axis=1)
        out = np.max(col, axis=1)
        out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)

        self.x = x
        self.arg_max = arg_max

        return out

    def backward(self, dout):
        dout = dout.transpose(0, 2, 3, 1)

        pool_size = self.pool_h * self.pool_w
        dmax = np.zeros((dout.size, pool_size))
        #flattenは構造を1次元配列に入れ直すこと
        dmax[np.arange(self.arg_max.size), self.arg_max.flatten()] = dout.flatten()
        dmax = dmax.reshape(dout.shape + (pool_size,)) 

        dcol = dmax.reshape(dmax.shape[0] * dmax.shape[1] * dmax.shape[2], -1)
        dx = col2im(dcol, self.x.shape, self.pool_h, self.pool_w, self.stride, self.pad)

        return dx

この後CNN実装と画像可視化していきますが、後日追加したいと思います。

機械学習アルゴリズムの復習(SVM)

SVMによるデータ分類を行います。

レーニングデータの準備

必要モジュールのimport

from sklearn import datasets
from sklearn import svm
import matplotlib.pyplot as plt
from sklearn import metrics

レーニングデータの準備

#データの準備
digits = datasets.load_digits()

# データ数の確認
n_samples = len(digits.data)
print("データ数:{}".format(n_samples))

#データの可視化
print(digits.data[0])

images_and_labels = list(zip(digits.images, digits.target))

for index, (image, label) in enumerate(images_and_labels[:10]): # enumerate:リストを順番に処理
    plt.subplot(2, 5, index + 1)
    plt.imshow(image, cmap=plt.cm.gray_r, interpolation='nearest')
    plt.axis('off')
    plt.title('Training: %i'% label)
plt.show()

f:id:thelarklife1021:20211001054328p:plain:h300

SVM のロード

clf = svm.SVC(gamma=0.001, C=100.)

6割のデータで学習し、4割のデータでテストする場合

学習実行

# 60%のデータで学習実行
clf.fit(digits.data[:int(n_samples * 6 / 10)], digits.target[:int(n_samples * 6 / 10)])

テストを実行

# 40%のデータでテスト
expected = digits.target[int(n_samples *-4 / 10):]
predicted = clf.predict(digits.data[int(n_samples *-4 / 10):])

print(clf,metrics.classification_report(expected, predicted))
print(metrics.confusion_matrix(expected, predicted))

予測結果を可視化

images_and_predictions = list(zip(digits.images[int(n_samples *-4 / 10):], predicted))
for index,(image, prediction) in enumerate(images_and_predictions[:12]):
 plt.subplot(3, 4, index + 1)
 plt.axis('off')
 plt.imshow(image, cmap=plt.cm.gray_r, interpolation='nearest')
 plt.title('Prediction: %i' % prediction)
plt.show()

下記の通り全て予測(Prediction)が(視認する限り)正確に学習されていることがわかる

f:id:thelarklife1021:20211001054918p:plain:h300

1割のデータで学習し、8割のデータでテストする場合

学習実行

# 10%のデータで学習実行
clf.fit(digits.data[:int(n_samples * 1 / 10)], digits.target[:int(n_samples * 1 / 10)])

テストを実行

# 90%のデータでテスト
expected = digits.target[int(n_samples *-9 / 10):]
predicted = clf.predict(digits.data[int(n_samples *-9 / 10):])

print(clf,metrics.classification_report(expected, predicted))
print(metrics.confusion_matrix(expected, predicted))

予測結果を可視化

images_and_predictions = list(zip(digits.images[int(n_samples *-9 / 10):], predicted))
for index,(image, prediction) in enumerate(images_and_predictions[:12]):
 plt.subplot(3, 4, index + 1)
 plt.axis('off')
 plt.imshow(image, cmap=plt.cm.gray_r, interpolation='nearest')
 plt.title('Prediction: %i' % prediction)
plt.show()

f:id:thelarklife1021:20211001060348p:plain:h300

このくらいだと1回の学習でもほぼ正確に予測できているように見える。 次はもっと複雑な形を選びたいと思います。

以上

2021年シドニーマラソンのバーチャルラン登録申し込みが開始されたようです

2021年のシドニーラソンのバーチャルラン登録申し込みが開始されました!

www.sydneymarathon.jp

私はフルマラソンで登録しておきました。
そろそろ気合い入れないと、、(最近雨が多いため中々走りずらい。。)