hiroportation

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

シドニーマラソン 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

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

機械学習アルゴリズムの復習(次元削除と主成分分析(PCA))

次元削除にて膨大なデータの主成分を探す。固有値分解はここで使います。


データセット読み込み

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn import preprocessing, decomposition

# データセット読み込み
df_wine_all=pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data', header=None)

df_wine_all.head(4)

取り込んだワインデータは前処理済みのもので先頭4行を抽出した結果は以下の通りとなる。

取り込んだワインを表示
   0      1     2     3     4    5   ...    8     9     10    11    12    13
0   1  14.23  1.71  2.43  15.6  127  ...  0.28  2.29  5.64  1.04  3.92  1065
1   1  13.20  1.78  2.14  11.2  100  ...  0.26  1.28  4.38  1.05  3.40  1050
2   1  13.16  2.36  2.67  18.6  101  ...  0.30  2.81  5.68  1.03  3.17  1185
3   1  14.37  1.95  2.50  16.8  113  ...  0.24  2.18  7.80  0.86  3.45  1480

[4 rows x 14 columns]


入力とラベルに分ける

X=df_wine_all.iloc[:,1:].values
Y=df_wine_all.iloc[:,0].values

print(X)

特徴リストの出力

[[1.423e+01 1.710e+00 2.430e+00 ... 1.040e+00 3.920e+00 1.065e+03]
 [1.320e+01 1.780e+00 2.140e+00 ... 1.050e+00 3.400e+00 1.050e+03]
 [1.316e+01 2.360e+00 2.670e+00 ... 1.030e+00 3.170e+00 1.185e+03]
~snip~
]

print(Y)

ラベルリストの出力

[1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3]


データの整形

sc=preprocessing.StandardScaler() # 標準化(平均0、分散1)
sc.fit(X)
X=sc.transform(X)

X

データを平均0、分散1(デフォルト)になるように整形します

array([[ 1.51861254, -0.5622498 ,  0.23205254, ...,  0.36217728,
         1.84791957,  1.01300893],
       [ 0.24628963, -0.49941338, -0.82799632, ...,  0.40605066,
         1.1134493 ,  0.96524152],


PCAを実行

6次元に次元圧縮する

pca = decomposition.PCA(n_components=6)
X_transformed = pca.fit_transform(X)

X_transformed

array([[ 3.18562979e+02,  2.14921307e+01,  3.13073470e+00,
        -2.50113758e-01,  6.77078222e-01,  5.68081040e-01],
       [ 3.03097420e+02, -5.36471768e+00,  6.82283550e+00,
        -8.64034749e-01, -4.86095978e-01,  1.43398712e-02],

結果の表示

print(pca.explained_variance_)
print(pca.components_)

f:id:thelarklife1021:20210901084617p:plain:h30

固有値固有ベクトルを求める

# 固有値λに相当
[4.73243698 2.51108093 1.45424187 0.92416587 0.85804868 0.64528221]

# 固有ベクトルvに相当(6次元)
[[ 0.1443294  -0.24518758 -0.00205106 -0.23932041  0.14199204  0.39466085
   0.4229343  -0.2985331   0.31342949 -0.0886167   0.29671456  0.37616741
   0.28675223]
 [-0.48365155 -0.22493093 -0.31606881  0.0105905  -0.299634   -0.06503951
   0.00335981 -0.02877949 -0.03930172 -0.52999567  0.27923515  0.16449619
  -0.36490283]
]

print(np.cumsum(pca.explained_variance_ratio_))

累積寄与率:どれだけ次元を削減したか(7~8割が目安)

[0.36198848 0.55406338 0.66529969 0.73598999 0.80162293 0.85098116]


結果のプロット

plt.subplot(2, 1, 2)
plt.scatter(X[:,9],X[:,12], c=Y)
plt.xlabel('color')
plt.ylabel('proline')
plt.show

f:id:thelarklife1021:20210901091302p:plain

%matplotlib inline
plt.figure(figsize=(10,10))
plt.subplot(2, 1, 1)
plt.scatter(X_transformed[:,0],X_transformed[:,1], c=Y)
plt.xlabel('PC1')
plt.ylabel('PC2')

PC1:第1成分
PC2:第2成分

f:id:thelarklife1021:20210901091052p:plain

以上