ターミナル設定
まず入れると幸せな道具(共通)
Homebrew がある前提です。
brew install zoxide fzf bat fd eza starship direnv # fzfキーバインド/補完の有効化(bash/zsh) $(brew --prefix)/opt/fzf/install
- zoxide:
z fooで学習ベース高速ディレクトリ移動。z foo:履歴から「foo」を含む最も関連性の高いディレクトリへジャンプ
- fzf: 履歴/ファイル/ブランチ等をあいまい検索
history | fzf
- bat:
catのリッチ版 - fd:
findの快適版 - exa:
lsの見やすい版(ls互換) - starship: 速くて美しいプロンプト
- direnv: ディレクトリ入ると自動で環境切替(.venvの自動アクティベートに最適)
A. zsh で“最短ゴール”セット
1) zsh プラグイン(補完&サジェスト)
軽量に zinit で入れます(oh-my-zsh不要)。
# なければ実施 brew install zsh mkdir -p ~/.zsh # zinit を入れる git clone https://github.com/zdharma-continuum/zinit.git ~/.zsh/zinit.git
~/.zshrc に以下を追記:
### zinit
source ~/.zsh/zinit.git/zinit.zsh
# 補完 & 色づけ
zinit light zsh-users/zsh-completions
zinit light zsh-users/zsh-syntax-highlighting
zinit light zsh-users/zsh-autosuggestions
# fzf-tab: <Tab> 補完を fzf 化
zinit light Aloxaf/fzf-tab
# ツール初期化
eval "$(zoxide init zsh)"
eval "$(starship init zsh)"
# fzf 便利キーバインド(Ctrl-R で履歴検索など)
# ※ fzf のインストールスクリプトで ~/.zshrc に入っていれば重複させないでOK
[ -f ~/.fzf.zsh ] && source ~/.fzf.zsh
# direnv(ディレクトリ毎に環境を自動切替)
eval "$(direnv hook zsh)"
# 好きなエイリアス
alias ls='eza --group-directories-first --icons'
# Ctrl-f で最近のファイルを fzf から開く
fzf-open-file() {
local file
file=$(fd . -t f | fzf) || return
code "$file"
}
zle -N fzf-open-file
bindkey '^F' fzf-open-file
# Ctrl-b で Git ブランチ切替
fzf-git-switch() {
local b
b=$(git for-each-ref --format='%(refname:short)' refs/heads/ | fzf) || return
git switch "$b"
}
zle -N fzf-git-switch
bindkey '^B' fzf-git-switch
これで 補完(Tab)、入力中のサジェスト(グレー文字)、色付き構文、fzf連携 が一気に揃います。
便利なキーバインド例(zsh)
Ctrl-f で最近のファイルを fzf から開く
- Ctrl-b で Git ブランチ切替
- Ctrl-R: 履歴検索
2) Starship プロンプト(見やすい&速い)
~/.config/starship.toml を作成:
# シンプルで情報量多めの例 add_newline = false [character] success_symbol = "❯" error_symbol = "✗" [git_branch] symbol = " " [git_status] conflicted = "!" ahead = "⇡" behind = "⇣" modified = "✚" staged = "●" untracked = "…" [directory] truncation_length = 3 truncate_to_repo = true [python] format = "[$symbol$virtualenv]($style) " symbol = " " # .venv を見つけたら表示 python_binary = "python3" # uv を使う場合のヒント表示(任意) [custom.uv] command = 'test -f pyproject.toml && echo "uv"' when = 'test -f pyproject.toml' format = "[$output] " # Google Cloud 情報がでないように [gcloud] disabled = true # AWS 情報がでないように [aws] disabled = true
.venv があればプロンプトに表示。Gitの状態もひと目で分かります。
仕上げチェックリスト ✅
開発時に見るコマンド・ショートカット集
Docker
$ docker build -t sample_docker . $ docker run -itd -v $(pwd):/opt/mnt -p 18080:8080 sample_docker $ docker ps $ docker exec -it aaa /bin/bash
Git
仕組み
- ワーキングツリー -<add>- インデックスエリア -<commit>- ローカルリポジトリ -<push>- リモートリポジトリ
- origin = リモートリポジトリの名前(エイリアス、インデックス)
git remote -vで確認可能git remote add aaa https://github.com/xxx/xxx.gitで追加可能。(aaaというインデックスで追加)
コマンド
クローン〜プッシュ
# クローン $ git clone -b development https://github.com/xxx/xxx.git # 新しいブランチを作って切り替え(1発) $ git switch -c feat/1 # == git checkout -b # (参考)ブランチだけ作成 $ git branch feat/1 # (参考)ブランチ切り替え $ git switch feat/1 # 確認 ブランチ一覧 $ git branch # 修正 略 # 確認 ワーキングツリーとインデックスエリアの差分 $ git diff # ワーキングツリー → インデックスエリア $ git add xxx.py # 確認 インデックスエリアとローカルリポジトリの差分 $ git diff --cached # インデックスエリア → ローカルリポジトリ $ git commit -m "add xxx.py refs #99" # 確認 ログ確認 $ git log --oneline # 確認 リモートリポジトリのブランチ一覧 $ git branch -r # ローカルリポジトリ → リモートリポジトリ ローカルの my-master ブランチを、origin 上の master ブランチに push する $ git push origin my-master:master $ git push origin feat/1:feat/1
取り込み
- fetch + merge = pull
- fetch は リモートリポジトリの最新情報をローカルに持ってくる。作業中のファイルやブランチには触らない(=ローカルには何もマージされない)、取得したリモートの状態は origin/main などの形で参照できる、マージやリベースは自分で別途実行する必要がある
# リモートリポジトリのmainをローカルリポジトリのorigin/mainブランチに $ git fetch origin main # リモートリポジトリのを全部持ってくる時は、 git fetch origin # ローカルリポジトリのorigin/mainを今いるリポジトリにマージ $ git merge origin/main
# リモートのmainブランチをローカルのmainに反映 $ git pull origin/main # githubでリモートのfeat/6ブランチがローカルのfeat/6ブランチより進んでいる $ git checkout feat/6 # feat/6ブランチに切り替えます $ git pull origin feat/6 # リモートの変更を取得してマージします
取り消し
# ワーキングツリーでの変更を取り消す $ git restore xxx.py # インデックスエリア → ワーキングエリア $ git reset -- xxx.py
その他
.gitkeepで空フォルダ追加.gitignoreで無視=git add の実行時に、インデックスエリアに追加されないようになる
Jupyter Notebook
Enter コマンドモードからエディットモードへ切り替える Esc エディットモードからコマンドモードへ切り替える a 現在のセルの上にセルを追加 b 現在のセルの下にセルを追加 dd 現在のセルを削除する m Markdownセルにする Shift + m 下のセルとマージする Shift + Tab Python構文の説明を表示する Ctrl + / 現在の行をコメントにする
poetry
$ poetry add --group <group> numpy $ poetry install --no-root --with <group> $ poetry lock
Terminal
# カーソル移動(先頭) Ctrl + a # カーソル移動(末尾) Ctrl + e # 削除(一文字) Ctrl + d # 削除(末尾まで) Ctrl + k # 履歴検索 Ctrl + r # 画面クリア Ctrl + l
tmux
# 注: prefixはCtrlとbを押し、一度キーを離して後続のキー(cとか)のみを押す # 新規セッション開始 tmux # 名前をつけて新規セッション開始 tmux new -s <セッション名> # セッションを中断 Ctrl-b d # セッションの一覧表示 tmux ls # セッションを再開 ※-t <対象セッション名>でセッション名の指定も可能 target tmux a # セッションを終了 ※-t <対象セッション名>でセッション名の指定も可能 tmux kill-session # ウィンドウを作成 Ctrl-b c # ウィンドウ番号を指定して移動 Ctrl-b 1 # 次のウィンドウに移動 (next) Ctrl-b n # 前のウィンドウに移動 (previous) Ctrl-b p # ペイン分割 prefix + "(横に線で縦に分割) prefix + %(縦に線で横に分割) # ペイン移動(矢印でも可) prefix + o(next) # ペイン削除 prefix + x
Vim
# 保存して終了 :wq # 挿入モードへ i # 新しい行を追加し挿入モードへ o # 矩形選択のビジュアルモードへ Ctrl + v # コマンドモードに戻る ESC # 次の行 j # 前の行 k # 行頭 0 # 行末 $ # 最初の行 gg # 88行目 88G # 最終行 G # 単語の置換(hageをhogeへ置換)。% はファイル全体を表す。 :%s/hage/hoge/g # 今いる行をコピー (yank) yy # カーソルの場所に、ペースト p # 1文字削除 x # 一行を削除する dd # 外部コマンドの実行 :!command # 直前の操作を取り消す u
VSCode
# Python実行
F5でデバック
# 引数設定
F5でデバック
→サイドバーでlaunch.jsonを作成する(実体は.vscode/launch.json)
→Python Fileを選択
→
{
"version": "0.2.0",
"configurations": [
{
...
"args": [
"3"
]
}
]
}
# ブレークポイント設定
F9でブレークポイント
もう一度F5で動き出す
# 補完
GitHub Copilot拡張
Ctrl+Enterキーを押すことにより、別なタブで複数の提案を一覧で見ることができる
# Copilot
Ctrl + I
Copilotでコメントを入れる
"""
コード全体に対して、次の条件を満たす改善を実施してください。
- Google スタイルの Docstring を記述する
- 使用しているがimportしていないライブラリをimportする
"""
# 定義ジャンプ
F12で定義ジャンプ
# 宣言へ移動
Ctrl + マウスクリック
# 関数の確認、引数オプション
# コメント有無の切り替え
Command + /
よく使うgitコマンド
仕組み
- ワーキングツリー - インデックスエリア - ローカルリポジトリ - リモートリポジトリ
- origin = リモートリポジトリのインデックス
git remote -vで確認可能git remote add aaa https://github.com/xxx/xxx.gitで追加可能。(aaaというインデックスで追加)
コマンド
クローン〜プッシュ
# クローン $ git clone -b development https://github.com/xxx/xxx.git # ブランチ作成 $ git branch feat/1 # ブランチ切り替え $ git switch feat/1 # 確認 ブランチ一覧 $ git branch # 修正 略 # 確認 ワーキングツリーとインデックスエリアの差分 $ git diff # ワーキングツリー → インデックスエリア $ git add xxx.py # 確認 インデックスエリアとローカルリポジトリの差分 $ git diff --cached # インデックスエリア → ローカルリポジトリ $ git commit -m "add xxx.py refs #99" # 確認 ログ確認 $ git log --oneline # 確認 リモートリポジトリのブランチ一覧 $ git branch -r # ローカルリポジトリ → リモートリポジトリ ローカルの my-master ブランチを、origin 上の master ブランチに push する $ git push origin my-master:master $ git push origin feat/1:feat/1
取り込み
- fetch + merge = pull
# リモートリポジトリのmainをローカルリポジトリのorigin/mainブランチに $ git fetch origin main # ローカルリポジトリのorigin/mainをmain(今いるリポジトリ)にマージ $ git merge origin/main
# リモートのmainブランチをローカルのmainに反映 $ git pull origin/main
取り消し
# ワーキングツリーでの変更を取り消す $ git restore xxx.py # インデックスエリア → ワーキングエリア $ git reset -- xxx.py
その他
.gitkeepで空フォルダ追加.gitignoreで無視=git add の実行時に、インデックスエリアに追加されないようになる
VertexAIの疑問点
※WIP
全体
- 対応リージョンの制限はあるか(tkyで利用できるか)
- AutoML、データセットなど含めて
- リリースバージョンはGAか
- Vertex AI Workbenchなど含めて
データ基盤(収集, 蓄積, 加工)
- データセットの利用シーン、利用方法は何か。BQ直接利用との違い(使い分け)は何か。
- 非構造化データ、アノテーションのため?
- AutoMLでは必須?
- データの実態はどこにある?同じデータセットを利用しても、元のデータ(BQなど)が変わっていると、結果が変わる?
ラベリング
データ分析環境
- ノートブックを作成するとエンドポイントが起動して、ブラウザでhttpアクセスできるのか
- メンバー間のノートブックの環境を合わせるにはどうすれば良いか。pip?コンテナ?パイプライン化までのフローを踏まえてどうするべきか。
- 従来のノートブックとWorkbenchの違いは何か
- マネージドとユーザ管理の違いは何か
- カスタムDockerイメージを使う場合の制約はあるか。特定ポートでJupyterを起動するなど。
- 実験管理はどうやるか。Experimentsは何ができるか。Metadataとの違いは何か。
- Metadataを実験管理ツールとして利用することはできるか
- ノートブックへのアクセス権はどう制御すれば良いか。
- ノートブックからのアクセス権はどう制御すれば良いか。
実験管理
- 実験管理できるか。実験管理できる項目は何か。
学習基盤
- データ分析の一環としてDSがジョブを投げ込むことができるか(ノートブックとの連動)
モデルレジストリ
- モデル管理として何の項目が管理できるか。メタデータ管理との使い分けは何か。
サービング
MLパイプライン
- 登場人物の全量
- ノートブックと実行環境をどう揃えるか
- AutoMLとの連携はどうやるか。スクラッチ実装と比較して(併用する場合)、手順のどこがどう変わってくるか。
- サービスのくくりが同じだから(AutoMLもVertexAI)、連携できるはず?
- バッチ実行もエンドポイントを起動する必要があるか。推論処理を実行したいときのパターンは何か。(オンライン含めて)
- ノートブックからパイプラインへの変換の過程、登場人物の全量。CIで完結するか、自前でファイルを書く必要があるか。
- ファクトリ関数を使うもの?CIで実行する?
- パイプラインの起動方法。SDKのみ?
- パイプライン起動のトリガー、スケジューラ
- Kubeflow Pipelines SDKのスケジューリング機能 やCloud Composer、Cloud Scheduler?それぞれの使い方
- コンポーネント間の中間生成物はどこに保存される?
- kfpはv2?
- コンポーネントそれぞれにスペックを割り当てることができるか。GPUの指定など。スケールアウトすることができるか(台数指定も)。
- エラー検知、切り分け、リランはどうやるか
- 途中からの実行はどうすれば良いか。エラーのジョブ実行結果は消えるのか。
- 実行結果のキャッシュとは何か。リランした時に処理がスキップされる?キャッシュを使う条件は?
- Kubeflow Pipelines SDKとTensorFlow Extended(TFX)の二通りがある?使い分けは?
- テラバイト単位の構造化データまたはテキストデータを処理するTensorflowならTFX?
- 引数はどこで渡してどう流れていく?
- Metricsはどう指定すれば何ができる?コンテナでは無理?
- デプロイするときは(エンドポイントを作成したい時は)、デプロイのコンテナ使ってさらにイメージを指定する?
- 実行はコンテナのみ?
- コンポーネントごとにGPU有無などのスペックの指定ができる?
- 後から確認できる項目は何か。コンポーネントを選択すると、パラメータや処理時間や実行ログ?
- AutoMLと併用した場合に、metaデータへの連携はどうなる?(前処理は自前実装、学習はAutoMLなど)
- リソース制限はあるか。無限にスケールするのか。
- 分析環境・学習パイプライン・推論パイプラインはPJを分けるべきか
- 特に、BQへ書き戻す場合の権限制御
- パイプラインの動作確認はどの環境で実施するべきか
- DataflowやDataprocとの併用はどうすれば良いか
- OpFunc関数とは何か
- 認証情報はどう取得するべきか(GCP)
- モデルのサイズ制限10GB(N1)?
- パイプラインの実行状況確認、停止、削除はできるか
- Trainingと自前実装の違いは何か。
- Predictionと自前実装の違いは何か。
- Metadataへ、任意のパラメータやmarkdownを連携・可視化することはできるか
- 依存関係の制御は、データのIn/Outか。マニュアルでの制御も可能か。
- GCSを使わずにBQに書き戻す場合もメタデータ管理は可能か
メタデータ管理
- In/Outデータ、コードバージョン、ハイパーパラメータ、精度、学習曲線、混同行列は管理できるか
- ノートブックからも利用できるか
CD(MLパイプライン, サービング)
- ノートブックの作成から、パイプライン実行までを自動化した場合の流れは何か。(手動の部分は手動)
- コミット、テスト、パイプラインのデプロイ=実行?、通知、承認
特徴量ストア
CI
- CIはどのサービスと連動させるのが良いか。
モニタリング(精度含む)
- モニタリングは具体的に何が監視できるか。
- 分布の比較をしてくれる?どことどこの比較?
- VertexAIのEndpointを利用する場合のみ?
ECSを復習してみた
構築手順・CICD設定
0. 全体構成
AmazonECS / Fargate 本番運用のための構築とデプロイ方法まとめから引用
1-1. Clusterの構築
- Cluster: TaskとServiceをグルーピングする概念。アプリケーションごと、環境ごとに用意する。
- Fargateだとネットワーキングのみ
1-2. DockerImageを作成し、ECRにpush
- ディレクトリ構成は要検討。アプリと分けるのもあり?DockerfileとTaskDefinitionをアプリごとにどこにおくか。
# イメージビルド $ docker build ./ -t ecr-cicd-example # ローカルで確認 $ docker run ecr-cicd-example # ECRリポジトリを作成しておく # ログイン aws ecr get-login-password --region us-east-2 | docker login --username AWS --password-stdin XXX.dkr.ecr.us-east-2.amazonaws.com # タグ付け(ローカルで) docker tag ecr-cicd-example:latestXXX.dkr.ecr.us-east-2.amazonaws.com/ecr-cicd-example:latest # プッシュ docker pushXXX.dkr.ecr.us-east-2.amazonaws.com/ecr-cicd-example:latest
1-3. TaskDefinitionの作成
- Task: コンテナの集合体
- TaskDefinition: docker-compose的なもの
- TaskDefinitionで定義するもの: タスクメモリ、CPU、コンテナ1のイメージURI・ポートマッピング・ログ設定
1-4. Serviceの作成と実行
- Serviceを作成せずTaskを実行することも可能(バッチ処理など)
- Service: 指定したtaskDefinitionからtaskを生成する
- Serviceで定義するもの: ネットワーク、VPC、タスク定義
- ネットワークモードで awsvpc を使う場合はプライベートサブネットを使いましょう
- ECS Serviceを作成する際に「deployment_controller」の設定で「type = "CODE_DEPLOY"」の指定
2-1. (手動)Dockerfileを更新し、ソースリポジトリにpush
2-2. (GitLab)DockerImageをビルドし、ECRにpush
- Docker Image TagとしてGitのCommit Hashを利用することでイメージを一意に特定できるようにします.
- TaskDefinitionも、コンテナイメージのURIを書き換えてS3にアップロードする
- AWS CLIのオプションで全てパラメータを渡すのは大変なので手元に「taskdef.json」というファイルを作成し, CLI Skeltonとして利用します
- S3バケットの「appspec.yaml」を更新します.。CodeDeployでDeploymentを作成する際にS3バケットにあるAppspecを指定して作成します.。なので事前に用意したS3バケットに上記で作成したTask Definitionの新しいリビジョンを指定した「appspec.yaml」を保存します。
# Task Definitionを登録する $ aws ecs register-task-definition \ --cli-input-json file://path/to/taskdef.json # appspec.yaml更新 aws s3 cp /path/to/appspec.yaml s3://YOUR_BUCKET_NAME/appspec.yaml
2-3. (CodeBuild)Greenをデプロイ
- テストポートで動作確認する
2-4. (CodeBuild)切り替え
3. コンテナログイン
- AppコンテナのRoleに対して、ecs-exec用のIAMポリシーを新たに追加
$ aws --profile=adachin ecs execute-command \
--region ap-northeast-1 \
--cluster adachin \
--task xxxxxxxxxxxxxxxxxxxxxx \
--container adachin-app \
--interactive \
--command "/bin/bash"
→シェルスクリプト化する
ポイント
ネットワーク
- VPCエンドポイントが色々必要
デプロイ
- CodeDeployと連動させて、ローリング or Blue/Green
- Blue/Greenがおすすめ
- ターゲットグループを二つ作っておいて切り替える
- 環境ごとにリポジトリ・デプロイのルールを変える(リポジトリポリシー、CodeDeploy)
- 開発:手動プッシュを許可
- ステージング:CI/CDからのプッシュのみ許可
- 本番: ステージングデプロイ済モデルのみデプロイ可能
- 環境ごとに固有のタグ識別文字を付与し、タグごとのライフサイクルポリシーを指定する
- タグは、環境識別子+コミットIDがおすすめ
- イメージの上書き禁止設定をする。(IMMUTABLE設定)
- pushはできるが更新されない?ので注意
- デプロイ前に承認アクションを設定する
- パスワードとかはSecret Managerでsecure stringとして管理する
- 今回追加されたDAEMONタイプ: ホストの増減に合わせて1コンテナずつ実行する。 従来のルールベースのECSサービス: REPLICAタイプ
- CI/CDをどこで実行するかを早めに決める。Jenkinsジョブなど。
監視
- SNSからChatbotに連携してSlack通知
- FireLensでログ運用する
- -> CloudWatch Logsよりおすすめ
- サイドカーパターンで配置する(設定をONにするだけ?)
- prefix-name/container-name/ecs-task-id
- ecs/ecr-cicd-example/aeecde8XXX
- nginxなどは、ログ出力先に「/dev/stdout」を設定する
- (コンテナとしてよくないが)ログが複数出るなら、awslogsで個別に設定する?
- CloudWatch Container Insightsを活用する
- CloudWatchLogs InsigthsでALBのログを見やすくする
セキュリティ
- ECS/FargateはPCI DSS準拠
- 金融サービスに関するWell-Architectedフレームワークレンズを参考にする
- ECRによる脆弱性スキャン+Trivyによる脆弱性スキャン
- dockleでベストプラクティスチェック
- -> CIの中でTrivy+dockle
- WAFをALBと連携させる
- コンテナレジストリの暗号化(作成時)
性能
- オートスケールはステップスケーリング or ターゲット追跡スケーリング
- ターゲット追跡スケーリングがおすすめ
踏み台
- SSMのセッションマネージャ
- SSMエージェントを仕込んでおく?
- →または、Amazon ECS Exec(最近はこちら?)
運用
- ECSはAWSの完全なサポートを受けられる(オーケストレータ部分含めて)
- FargateはEC2よりも割高なので注意
- FargateはOSのセキュリティパッチ適用や各種ライブラリのアップデート作業が不要になる(運用工数削減)
- 開発・ステージング環境はLambdaで稼働時間帯を調整する
- 切り分け
$ aws ecs list-clusters --region us-east-2 $ aws ecs list-tasks --cluster [クラスター名] --desired-status STOPPED --max-items 3 --region us-east-2 $ aws ecs describe-tasks \ --cluster MyCluster \ --tasks arn:XXX \ --region us-east-2
その他
- sshでない。execを使う。踏み台を経由する?証跡の観点から。
- ログが分かれるので、標準出力だけでは運用できない?ファイルごとにさらに出力先を分けたい?
- →awslogsでやるしかない?awslogsのロググループ等どうするか
- 開発フローどうするか。アプリ側にどう案内するか。CI/CDは必要。コンテナのテストは必要(Fargate上での切り分けが辛いので)
- →さらに、EC2モードでデプロイする準備もしておく
- Jenkinsサーバをどこにおくか。DEVとPRD。どっちにどのジョブを作るか。
- ビルドもFargate?kanikoでビルドはできそうだが実行できるか?pushを挟んでGitLab runnerの他ステップで実行する?→DEVにもSTGにもpushすることになる?
- コンテナごとにログが分かるか→ログタスクさえわかればログインできる
- 一台だけ異常があった場合に、それだけ落とすとかできるか。
- 環境ごとの適用状況の差分確認をどうするか
参考
MLOps導入に向けたヒアリングチェックリスト
はじめに
- MLOpsの導入・改善を頼まれたときに、初手でヒアリングすることの簡単なチェックリストです
ヒアリング事項
前提条件
(現時点/これから)
- ☑︎プロジェクト(モデル)の種類
- 需要予測 / 類似画像検索, etc
- ☑︎データの種類
- テーブルデータ / 画像 / テキスト, etc
- ☑︎利用者数(DS, SWE, SRE)
- ☑︎環境(オンプレ/クラウド)は何か
- ☑︎プログラミング言語
- Python / 推論部分はGo, etc
- ☑︎フレームワーク
- Tensorflow / PyTorch, etc
- ☑︎その他、ツール
- GitHub, etc
スコープ
(現時点/これから)
基本的にはGoogleのこれの通りのステップで導入を進めて行けば良いと思っています。
レベル0: 手動プロセス
- ☑︎データ基盤(収集, 蓄積, 加工)
- ☑︎ラベリング
- ☑︎データ分析環境
- ☑︎実験管理
- ☑︎学習基盤
- ☑︎モデルレジストリ
- ☑︎サービング
レベル1: ML パイプラインの自動化
- ☑︎MLパイプライン
- ☑︎メタデータ管理
- ☑︎CD(MLパイプライン, サービング)
- ☑︎特徴量ストア
レベル2: CI / CD パイプラインの自動化
- ☑︎CI
- ☑︎モニタリング(精度含む)
その他
- ☑︎背景・課題
- ☑︎作業内容と役割分担
- ☑︎想定アウトプット
- ☑︎スケジュール
- ☑︎体制
