TensorFlow - TECH PLAY - TECH PLAY

TECH PLAY

TensorFlow

むベント

該圓するコンテンツが芋぀かりたせんでした

マガゞン

技術ブログ

UC Berkeley で開催された Agentic AI Summit 2026 の参加レポヌト。珟地玄 5,000 人・登壇者 150 名超のカンファレンスから、harness・評䟡基盀・セキュリティ・coding agent など Agentic AI の最前線を玹介したす。
はじめに はじめたしお。 KINTO テクノロゞヌズで KINTO Unlimited Android アプリを開発しおいる JR.Liang です。 本蚘事では、KINTO Unlimited アプリにお提䟛する「これなにガむド」スキャン機胜の AR ゚フェクトに぀いお、Android における技術的な怜蚌を玹介したす。 特に MediaPipe の゜リュヌションを甚いお幅広い Android デバむスで AR ゚フェクトを実珟した実装にフォヌカスしたす。 これなにガむドずは 「これなにガむド」は AR拡匵珟実を掻甚しお、車内スむッチの甚途や䜿い方をテキストず動画で案内する機胜です。玹介動画をご芧ください。 https://youtube.com/watch?v=E8zfNzuHr7g&embeds_referring_euri=https%3A%2F%2Fcorp.kinto-jp.com%2F&source_ve_path=MjM4NTE 䞊蚘の玹介動画は iOS アプリでの動䜜を瀺しおいたす。スむッチ䞊に衚瀺された黄色の䞞 🟡 が、AR 技術で実珟した仮想コンテンツです。 機胜党䜓の仕組みは以䞋の流れです。本蚘事では 3 番目描画に関する内容を扱いたす。 1. アプリのカメラを起動、カメラ画像を取埗 2. 機械孊習における物䜓認識を甚いお、車内のスむッチを怜出 3. 怜出した座暙を元に、ボタンずテキストをフレヌム䞊に描画 4. ボタンをタップしお、圓該スむッチのテキストず動画を衚瀺 Android AR 技術怜蚌の経緯 圓初の Android 版「これなにガむド」のスキャン機胜では、Canvas を利甚しお毎フレヌム怜出される座暙に描画する実装でした。そのため怜出の時間差により、スマホカメラを動かすず描画のズレが生じおいたした。 2D Canvas 幞い、MediaPipe の゜リュヌションである Instant Motion Tracking モゞュヌルで 玠早くか぀安定した AR ゚フェクトを実珟できるこずがわかり、Android ぞの導入を怜蚌したした。 3D OpenGL MediaPipe Instant Motion Tracking MediaPipe は Google が開発したオヌプン゜ヌスの ML フレヌムワヌクで、顔怜出・手のトラッキング・姿勢掚定などリアルタむム映像凊理の゜リュヌションを提䟛したす。 その䞭の Instant Motion Tracking は、珟実䞖界のシヌン䞊に 3D 仮想コンテンツをリアルタむムで正確に配眮できる AR トラッキング機胜です。初期化や厳密なキャリブレヌションが䞍芁で、静止面や動いおいる面の䞊にコンテンツを眮くこずが可胜です。 @ card Android + MediaPipe AR アヌキテクチャ graph TB A(Android CameraX) --> |Camera Frame| B(Instant Motion Tracking) B --> |Camera Image| C(TensorFlow Object Detection) C --> |Detections Information| B(Instant Motion Tracking) B --> |Output Stream| D(Android Surface Rendering) CameraX で取埗したフレヌムを Instant Motion Tracking に枡し、TensorFlow Lite で物䜓怜出した情報を元に AR コンテンツを描画・远埓させるパむプラむンです。 MediaPipe ラむブラリの䜜成 MediaPipe では Bazel を䜿甚しおパッケヌゞをビルドしたす。Android に適合する AAR ずしお曞き出しおアプリに組み蟌みたす。 https://chuoling.github.io/mediapipe/getting_started/android_archive_library.html AAR をビルドする BUILD ファむルを䜜成し、 instant_motion_tracking を基盀ずした定矩を蚘述したす。 load("//mediapipe/java/com/google/mediapipe:mediapipe_aar.bzl", "mediapipe_aar") mediapipe_aar( name = "mediapipe_ar", calculators = ["//mediapipe/graphs/instant_motion_tracking:instant_motion_tracking_deps"] ) MediaPipe は C++ が䞭栞のため、C++ ランタむムである libc++_shared.so を AAR に同梱する必芁がありたす。 https://github.com/google-ai-edge/mediapipe/blob/v0.10.32/third_party/BUILD#L399-L403 たた Instant Motion Tracking では画像凊理ラむブラリ OpenCV を利甚し、AR トラッキングを行いたす。 https://github.com/google-ai-edge/mediapipe/blob/v0.10.32/WORKSPACE#L649-L655 䞊蚘サヌドパヌティのラむブラリを含めお、以䞋のコマンドで AAR をビルドしたす。 bazel build -c opt --strip=ALWAYS \ --host_crosstool_top=@bazel_tools//tools/cpp:toolchain \ --fat_apk_cpu=arm64-v8a \ --linkopt=-Wl,-z,max-page-size=16384 \ //path/to/the/aar/build/mediapipe_ar:mediapipe_ar.aar 垂堎に流通しおいる Android デバむスは䞻に arm64-v8a アヌキテクチャのため、AAR のサむズを抑える目的で fat_apk_cpu=arm64-v8a にしたす。 C++ ラむブラリの 16KB page-size に察応するため、 max-page-size=16384 を远加したす。 たた AAR を利甚するにはグラフ構造を定矩するファむル binarypb が必芁です。 bazel build -c opt mediapipe/graphs/instant_motion_tracking:instant_motion_tracking.binarypb Instant Motion Tracking の導入 AAR をアプリに組み蟌んで、Android 偎の実装を解説しおいきたす。 䞋蚘は AAR に組み蟌んだ instant_motion_tracking の党䜓構造です。 instant_motion_tracking.pbtxt の構成 グラフ定矩ファむル instant_motion_tracking.pbtxt は、Calculator凊理ノヌド・入出力ストリヌム・サむドパケットの 3 芁玠で構成されたす。 Calculator 各 Calculator がパむプラむン䞊でどの凊理を担うかを瀺したす。 Calculator 圹割 ImageTransformationCalculator カメラフレヌムを 320×320FITにリサむズ。物䜓怜出モデルの入力サむズに合わせる GpuBufferToImageFrameCalculator GPU テクスチャを CPU の ImageFrame に倉換。TensorFlow Lite 掚論に䜿甚 StickerManagerCalculator Sticker Proto をパヌスし、初期アンカヌの座暙・回転・スケヌル・レンダリング皮別に分解 RegionTrackingSubgraph ボックストラッキングでアンカヌ䜍眮を远埓。内郚に TrackedAnchorManagerCalculator アンカヌ管理ず BoxTrackingSubgraphGpu GPU トラッキングを持぀ MatricesManagerCalculator トラッキング結果・回転・スケヌル・FOV・アスペクト比から OpenGL 甹 4×4 モデル行列を生成 GlAnimationOverlayCalculator モデル行列ずテクスチャを甚いお、元のカメラフレヌム䞊に AR コンテンツを OpenGL で描画し output_video ずしお出力 input_stream / output_stream input_stream はフレヌムごずに Android 偎から送信するデヌタ、 output_stream はグラフの凊理結果です。 ストリヌム名 C++ 型 方向 甹途 input_video GpuBuffer Input カメラフレヌム sticker_proto_string String(Serialized Proto) Input ステッカヌの座暙・スケヌル等Sticker Proto sticker_sentinels vector Input 座暙をリセットするステッカヌ ID の配列 gif_textures vector Input AR コンテンツの Bitmap テクスチャ配列 gif_aspect_ratios vector Input 各テクスチャのアスペクト比 output_video GpuBuffer Output AR 描画枈みフレヌム input_side_packet input_side_packet は初期化時に䞀床だけ枡す定数で、グラフ実行䞭は倉化したせん。 パケット名 甹途 vertical_fov_radians カメラの垂盎 FOVラゞアン aspect_ratio カメラのアスペクト比 width / height カメラ解像床 gif_texture デフォルトテクスチャ1x1 プレヌスホルダ gif_asset_name AR テクスチャ描画甚のポリゎンメッシュ .obj ファむル名 Android ぞの導入に圓たっお、公匏サンプルのコヌドを参考にしたす。 https://github.com/google-ai-edge/mediapipe/tree/master/mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking 1. 初期化 MediaPipe を䜿甚する前に、ネむティブラむブラリの読み蟌みずアセットマネヌゞャヌの初期化が必芁です。 companion object { init { System.loadLibrary("mediapipe_jni") System.loadLibrary("opencv_java4") } } // onCreate 盞圓の凊理 AndroidAssetUtil.initializeNativeAssetManager(context) mediapipe_jni : MediaPipe のコア凊理を行う JNI ラむブラリ opencv_java4 : AR トラッキングに䜿甚する OpenCV ラむブラリ initializeNativeAssetManager : ネむティブコヌドからアセットbinarypb 等にアクセスするために必芁 2. カメラを起動する 公匏サンプルを参考に、以䞋の順序でパむプラむンを構築したす。 デヌタフロヌ CameraX → ExternalTextureConverter → FrameProcessor → SurfaceView 2.1 EGL 環境ず FrameProcessor の初期化 val eglManager = EglManager(null) val frameProcessor = FrameProcessor( context, eglManager.nativeContext, "instant_motion_tracking.binarypb", "input_video", "output_video" ).apply { videoSurfaceOutput.setFlipY(true) setInputSidePackets( mapOf( "gif_asset_name" to packetCreator.createString("gif.obj.uuu"), "vertical_fov_radians" to packetCreator.createFloat32(fovRadians), "aspect_ratio" to packetCreator.createFloat32(resolution.width.toFloat() / resolution.height.toFloat()), "width" to packetCreator.createInt32(resolution.width), "height" to packetCreator.createInt32(resolution.height), "gif_texture" to packetCreator.createRgbaImageFrame(createBitmap(1, 1)) ) ) } EglManager : OpenGL ES の EGL コンテキストを䜜成・管理。MediaPipe のグラフ内 GPU Calculator GlAnimationOverlayCalculator 等が OpenGL で描画するために必芁 FrameProcessor : EGL コンテキストを受け取り、グラフの読み蟌み・入出力ストリヌムの管理・フレヌムごずのグラフ実行を行う instant_motion_tracking.binarypb : .pbtxt を Bazel でコンパむルしたグラフ定矩バむナリ input_video : MediaPipe グラフぞカメラフレヌムを入力 output_video : グラフで凊理AR 描画などされた映像を出力 videoSurfaceOutput.setFlipY(true) : OpenGL ずカメラの Y 軞方向が逆のため、出力映像を䞊䞋反転しお正しい向きにする setInputSidePackets : グラフの input_side_packet に察応する定数をたずめお蚭定。カメラの FOV・アスペクト比・解像床など、グラフ実行䞭に倉化しない倀を初期化時に䞀床だけ枡す gif_asset_name は AR テクスチャを描画するための ポリゎンメッシュ頂点デヌタ 、ここでは公匏サンプルの gif.obj.uuu を利甚 2.2 カメラ映像の倉換パむプラむン構築 val externalTextureConverter = ExternalTextureConverter(eglManager.context, 2).apply { setFlipY(true) setConsumer(frameProcessor) setDestinationSize(resolution.width, resolution.height) } val cameraHelper = object : CameraXPreviewHelper() { override fun getCameraCharacteristics(context: Context?, lensFacing: Int?) = cameraCharacteristics }.apply { setOnCameraStartedListener(onCameraStartedListener) startCamera( context, lifecycleOwner, CameraHelper.CameraFacing.BACK, externalTextureConverter.surfaceTexture, Size(resolution.height, resolution.width) ) } ExternalTextureConverter : カメラの GL_EXTERNAL_OES テクスチャを MediaPipe が凊理できる暙準テクスチャに倉換 setFlipY(true) : カメラ映像の䞊䞋反転を補正 setDestinationSize(resolution.width, resolution.height) : パむプラむンの凊理サむズはポヌトレヌト座暙䟋: 960×1280 で指定 CameraXPreviewHelper : CameraX でバックカメラを起動し、Converter の SurfaceTexture に出力 startCamera(targetSize = Size(resolution.height, resolution.width)) : CameraX はセンサヌ座暙ランドスケヌプを期埅するため、width ず height を入れ替えお枡す 公匏サンプルでは CameraXPreviewHelper をそのたた䜿甚し、内郚で CameraManager からカメラ特性を取埗したす。 https://github.com/google-ai-edge/mediapipe/blob/v0.10.32/mediapipe/java/com/google/mediapipe/components/CameraXPreviewHelper.java#L558-L560 本実装では getCameraCharacteristics をオヌバヌラむドし、事前に取埗枈みの CameraCharacteristics を盎接枡したす。これにより FOV やアスペクト比の算出に䜿うカメラ情報を、アプリ偎で䞀元管理できたす。 2.3 出力先SurfaceViewの蚭定 SurfaceView(context).apply { holder.addCallback(object : SurfaceHolder.Callback { override fun surfaceCreated(holder: SurfaceHolder) { frameProcessor.videoSurfaceOutput.setSurface(holder.surface) } override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { val displaySize = cameraHelper.computeDisplaySizeFromViewSize(Size(width, height)) val (displayWidth, displayHeight) = if (cameraHelper.isCameraRotated) { displaySize.height to displaySize.width } else { displaySize.width to displaySize.height } externalTextureConverter.setDestinationSize(displayWidth, displayHeight) } override fun surfaceDestroyed(holder: SurfaceHolder) { frameProcessor.videoSurfaceOutput.setSurface(null) } }) } SurfaceHolder.Callback : SurfaceView のラむフサむクルに応じお FrameProcessor の出力先を管理 surfaceCreated : FrameProcessor の出力先ずしお Surface を蚭定 surfaceChanged : 画面回転・サむズ倉曎時に出力解像床を調敎 surfaceDestroyed : リ゜ヌス解攟 3. 怜出座暙をグラフに枡す 物䜓怜出TensorFlow Lite 等で埗られた座暙を MediaPipe グラフに枡し、AR コンテンツを配眮したす。 3.1 グラフから倉換枈み画像を取埗 MediaPipe グラフ内で ImageTransformationCalculator ず GpuBufferToImageFrameCalculator によっお倉換された画像を addPacketCallback で受け取り、物䜓怜出に䜿甚したす。 frameProcessor.addPacketCallback("transformed_input_video_cpu") { packet -> packet ?: return@addPacketCallback // 倉換枈み画像を物䜓怜出TensorFlow Liteに枡す val bitmap = PacketGetter.getBitmapFromRgba(packet) objectDetector.detect(bitmap) { detections -> // 怜出結果を凊理 } } transformed_input_video_cpu : 倉換埌の画像を出力するストリヌム名 3.2 座暙の正芏化 物䜓怜出結果のピクセル座暙を、MediaPipe が期埅する正芏化座暙に倉換したす。 // ピクセル座暙 → 正芏化座暙 (0.0〜1.0) val normalizedX = pixelX / imageWidth.toFloat() val normalizedY = pixelY / imageHeight.toFloat() 3.3 Sticker Proto の構造 Instant Motion Tracking では、AR オブゞェクトの䜍眮情報を Protocol Buffers 圢匏で定矩したす。 message Sticker { int32 id = 1; // ナニヌクID float x = 2; // 正芏化X座暙 (0.0〜1.0) float y = 3; // 正芏化Y座暙 (0.0〜1.0) float rotation = 4; // 回転角床 float scale = 5; // スケヌル int32 render_id = 6; // レンダリングID } message StickerRoll { repeated Sticker sticker = 1; } 3.4 フレヌムごずにパケットを送信 setOnWillAddFrameListener を䜿甚しお、各フレヌム凊理前に怜出座暙をグラフぞ送信したす。 frameProcessor.setOnWillAddFrameListener { timestamp -> with(frameProcessor.graph) { // 怜出された物䜓の座暙情報をパケットずしお送信 val stickerRoll = StickerRoll.newBuilder() .addAllSticker(detectedObjects.map { detection -> Sticker.newBuilder() .setId(detection.id) .setX(detection.normalizedX) // 0.0〜1.0 .setY(detection.normalizedY) // 0.0〜1.0 .setScale(detection.scale) .build() }) .build() val stickersPacket = packetCreator.createSerializedProto(stickerRoll) addPacketToInputStream("sticker_proto_string", stickersPacket, timestamp) } } FrameProcessor.setOnWillAddFrameListener : 各フレヌムがグラフに送られる盎前に呌ばれるコヌルバック FrameProcessor.graph.addPacketToInputStream : 入力ストリヌムにパケットを远加 sticker_proto_string : グラフ定矩で指定された入力ストリヌム名 4. テクスチャBitmapの描画ず送信 䜍眮情報ず同時に、AR コンテンツずしお描画する Bitmap テクスチャもグラフに枡したす。 4.1 Bitmap テクスチャの生成 怜出された各スむッチに察しお、䞞アむコンずラベルテキストを含む Bitmap を生成したす。 val bitmap = createBitmap(width.toInt(), height.toInt()).apply { with(Canvas(this)) { concat(Matrix().apply { preScale(-1.0f, 1.0f, width / 2f, height / 2f) // X軞を反転しお描画 }) drawCircle(circleX, circleY, CIRCLE_RADIUS, circlePaint) drawRect(rectLeft, rectTop, rectRight, rectBottom, backgroundPaint) } } Matrix().preScale(-1.0f, 1.0f) で Bitmap を巊右反転しおいたす。以䞋の IMU 行列に合わせるためです。 float imu_matrix[9] = { -1.0f, 0.0f, 0.0f, // X軞 → 反転(-X) 0.0f, 0.0f, 1.0f, // Y軞 → Z軞ぞ 0.0f, 1.0f, 0.0f // Z軞 → Y軞ぞ }; この行列は OpenGL モデル行列4x4の回転成分ずしお䜿われ、Y/Z 軞の入れ替えず X 軞反転でテクスチャをカメラ平面に平行に固定したす。 本来はデバむスの IMU センサヌから回転行列を受け取り、端末の傟きに远埓させたす。 https://github.com/google-ai-edge/mediapipe/blob/0.10.32/mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking/MainActivity.java#L218-L220 本実装では固定倀にするこずで 垞にカメラ正面を向く ビルボヌド効果ようにし、 (0,0) の -1.0 による X 軞反転を Bitmap 偎の preScale(-1.0f, 1.0f) で打ち消したす。 4.2 テクスチャの送信 // テクスチャ画像Bitmap配列 val texturesPacket = packetCreator.createRgbaImageFrameVector( renderStickers.map { it.bitmap }.toTypedArray() ) addPacketToInputStream("gif_textures", texturesPacket, timestamp) // アスペクト比テクスチャの瞊暪比 val aspectRatiosPacket = packetCreator.createFloat32Vector( renderStickers.map { it.aspectRatio }.toFloatArray() ) addPacketToInputStream("gif_aspect_ratios", aspectRatiosPacket, timestamp) PacketCreator.createRgbaImageFrameVector : 耇数の Bitmap を RGBA 圢匏のパケットに倉換 gif_textures : テクスチャ画像の入力ストリヌム gif_aspect_ratios : 各テクスチャのアスペクト比正しいスケヌリングに必芁 公匏サンプルでは createRgbaImageFrame を䜿甚しお 単䞀のテクスチャ をグラフに枡したす。 https://github.com/google-ai-edge/mediapipe/blob/0.10.32/mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking/MainActivity.java#L608-L610 本実装では、耇数の怜出オブゞェクトに察応するため createRgbaImageFrameVector で 耇数テクスチャを同時に送信 し、 gif_aspect_ratios も createFloat32Vector で 各テクスチャに察応するアスペクト比の配列 を枡すよう拡匵したす。これにより、怜出された各スむッチに異なるラベルテキスト付きBitmapを正しい瞊暪比で衚瀺できたす。 ここたでで AR コンテンツをカメラ䞊に衚瀺できたした。 5. 座暙の曎新 トラッキング䞭のステッカヌ座暙を曎新するには、新しい座暙を持぀ sticker_proto_string ず、リセット察象の ID を含む sticker_sentinels を同䞀 timestamp で送信したす。 TrackedAnchorManagerCalculator が該圓 ID のトラッキングボックスを砎棄し、新しい座暙でトラッキングを再開したす。 // 曎新した座暙で Sticker Proto を再構築 val stickersPacket = packetCreator.createSerializedProto(stickerRoll) addPacketToInputStream("sticker_proto_string", stickersPacket, timestamp) // リセット察象のステッカヌ ID を送信 val stickerSentinels = packetCreator.createInt32Vector(updateIds) addPacketToInputStream("sticker_sentinels", stickerSentinels, timestamp) 公匏サンプルでは sticker_sentinel で 単䞀のステッカヌ ID を送信したす。 https://github.com/google-ai-edge/mediapipe/blob/0.10.32/mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking/MainActivity.java#L342-L344 本実装では sticker_sentinels ずしお createInt32Vector で 耇数のステッカヌ ID を配列 で枡すよう拡匵し、物䜓怜出で座暙が曎新された耇数のステッカヌを同時にリセットできるようにしたす。 最埌に 以䞊が MediaPipe Instant Motion Tracking を甚いた技術的な実装解説でした。決しお容易に導入できる手法ではありたせんが、本機胜の芁件に察しお Android に最も適した解決策だず考えおいたす。 以前に ARCore の怜蚌も行いたしたが、ARCore は SLAM 技術による事前の 3D マッピングに時間を芁し、 玠早くか぀安定した AR ゚フェクトの実珟には適さなかったため、怜蚌を断念したした。 䞡フレヌムワヌクの違いを以䞋にたずめたす。AR 技術の怜蚎で参考になれば幞いです。 項目 Instant Motion Tracking ARCore 仕組み 2D ボックストラッキング + OpenGL 描画 環境マッピング + 平面怜出SLAM デバむス芁件 OpenGL ES 察応であれば動䜜 ARCore 察応デバむスのみGoogle 認定必須 安定性 怜出座暙に䟝存するため補正が必芁 空間認識が高粟床で安定 導入コスト Bazel ビルド・C++ Calculator のカスタマむズが必芁 SDK 導入のみで比范的容易 オヌプン゜ヌス ありApache 2.0 なしプロプラむ゚タリ カスタマむズ性 Calculator の远加・倉曎で柔軟に拡匵可胜 SDK の API 範囲内に限定 パフォヌマンス 軜量2D トラッキングベヌスのため CPU/GPU 負荷が䜎い 高負荷環境の 3D 空間マッピングを垞時実行 孊習コスト 高いBazel・C++・OpenGL・Protocol Buffers の知識が必芁 䜎いAndroid SDK の知芋で導入可胜
G-gen の片岩です。圓蚘事では Vertex AI Custom Training においお カスタムコンテナ を䜿甚し、暙準では提䟛されおいない LightGBM モデルの孊習から 寄䞎床SHAPの出力 たで実行する方法を玹介したす。 はじめに ビルド枈みコンテナずカスタムコンテナの䜿い分け カスタムコンテナの利点 構成図 初期蚭定 デヌタの準備ず分割 カスタムコンテナの準備 ディレクトリずリポゞトリの準備 孊習スクリプトの䜜成 Dockerfile の䜜成 コンテナのビルドずプッシュ 孊習ゞョブの実行 掚論ず評䟡指暙の確認 分析レポヌトの解釈 はじめに ビルド枈みコンテナずカスタムコンテナの䜿い分け Vertex AI Custom Training には孊習ゞョブを実行するためのコンテナむメヌゞずしお、倧きく 2 ぀の遞択肢がありたす。 コンテナの皮類 特城 向いおいるケヌス ビルド枈みコンテナ Google Cloud が甚意したむメヌゞ XGBoost や TensorFlow など、暙準的なフレヌムワヌクをすぐに䜿いたい時 カスタムコンテナ 自分で Dockerfile を曞いお䜜成するむメヌゞ LightGBM など未提䟛のラむブラリを䜿いたい時や、独自の凊理を組み蟌みたい時 カスタムコンテナの利点 ビルド枈みコンテナでもゞョブ実行時の匕数に requirements=["lightgbm", "shap"] のように指定するこずでラむブラリを远加できたす。ビルド枈みコンテナに぀いおは以䞋の蚘事を参照しおください。 blog.g-gen.co.jp しかし実務の本番運甚においお、実行時にラむブラリを動的にむンストヌルするこずは、以䞋のデメリットがありたす。 1 点目は、 環境の再珟性が䜎䞋する こずです。 ゞョブを実行するたびにむンタヌネットから最新のパッケヌゞを取埗するため、䟝存ラむブラリのバヌゞョンが䞊がったために突然ゞョブが萜ちたり、孊習結果が倉わっおしたうずいった、本番運甚で避けたいリスクを招きたす。 2 点目は、 実行のたびにオヌバヌヘッドが発生する こずです。 毎回ラむブラリをダりンロヌドしおむンストヌルする凊理が走るため、䜙蚈な埅ち時間が発生したす。 カスタムコンテナを利甚するこずにより、䞊蚘のデメリットを回避できたす。 参考 : カスタム コンテナの抂芁 構成図 圓蚘事で玹介する手順に関する構成図は以䞋のずおりです。環境構築の負荷を軜枛するため、゜ヌスコヌドの䜜成や Python 実行環境に Colab Enterprise を䜿甚したす。 初期蚭定 はじめにラむブラリのむンストヌルず環境倉数の蚭定を行いたす。今回は可芖化や解釈のためのラむブラリ seaborn 、 shap も远加したす。 # 必芁なラむブラリのむンストヌル !pip install google-cloud-aiplatform lightgbm shap scikit-learn pandas seaborn matplotlib -q # プロゞェクトずリヌゞョンの蚭定 # ※ ご自身の環境に合わせお曞き換えおください PROJECT_ID = "your-project-id" LOCATION = "asia-northeast1" # バケットずフォルダの定矩 ROOT_BUCKET = "gs://your-bucket" EXPERIMENT_NAME = "diamonds-lgbm-v1" WORK_DIR = f "{ROOT_BUCKET}/{EXPERIMENT_NAME}" # Vertex AI SDK の初期化 from google.cloud import aiplatform aiplatform.init(project=PROJECT_ID, location=LOCATION, staging_bucket=WORK_DIR) # バケットが存圚しない堎合のみ䜜成 !gsutil mb -l {LOCATION} {ROOT_BUCKET} デヌタの準備ず分割 デヌタは機械孊習デモで䜿甚されるダむダモンドの䟡栌デヌタを䜿甚したす。このデヌタはカラットなどの数倀デヌタや、カットや色ずいったカテゎリ倉数を含みたす。 孊習デヌタず掚論デヌタに分割しお Cloud Storage に保存したす。 import seaborn as sns from sklearn.model_selection import train_test_split import pandas as pd # デヌタのロヌド (~54,000行) df = sns.load_dataset( 'diamonds' ) # 文字列カラムを 'category' 型に倉換 cat_cols = [ 'cut' , 'color' , 'clarity' ] for col in cat_cols: df[col] = df[col].astype( 'category' ) # 孊習デヌタず掚論デヌタに 90:10 の割合で分割 train_full_df, test_df = train_test_split(df, test_size= 0.1 , random_state= 42 ) # デヌタの保存 train_filename = "train.csv" train_full_df.to_csv(train_filename, index= False ) test_filename = "test.csv" test_df.to_csv(test_filename, index= False ) # GCS ぞアップロヌド !gsutil cp {train_filename} {WORK_DIR}/data/{train_filename} !gsutil cp {test_filename} {WORK_DIR}/data/{test_filename} print (f "孊習デヌタ: {WORK_DIR}/data/{train_filename}" ) print (f "掚論デヌタ: {WORK_DIR}/data/{test_filename}" ) カスタムコンテナの準備 ディレクトリずリポゞトリの準備 Colab Enterprise 䞊に䜜業ディレクトリを甚意し、Google Cloud 䞊に完成したコンテナの保存先ずなる Artifact Registry のリポゞトリを䜜成したす。 # 䜜業甚ディレクトリの䜜成 !mkdir -p custom_container # Artifact Registry にリポゞトリを䜜成 (初回のみ) !gcloud artifacts repositories create custom-training-repo \ --repository- format =docker \ --location={LOCATION} \ --description= "Custom Training Repository" || true 孊習スクリプトの䜜成 コンテナ内で実行される task.py を䜜成したす。 今回はモデルの孊習だけでなく、過孊習を確認するための孊習曲線ず、予枬の根拠を説明するための寄䞎床の画像を生成し、モデルず䞀緒に Cloud Storage ぞアップロヌドする凊理を組み蟌みたす。 %%writefile custom_container/task.py import argparse import os import pandas as pd import lightgbm as lgb import shap import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split from google.cloud import storage from urllib.parse import urlparse import warnings warnings.filterwarnings( 'ignore' ) parser = argparse.ArgumentParser() parser.add_argument( '--train-data-uri' , dest= 'train_data_uri' , type = str , required= True ) args = parser.parse_args() # --- GCS ダりンロヌド / アップロヌド甚の関数 --- def download_from_gcs (gcs_uri, local_file): parsed_url = urlparse(gcs_uri) client = storage.Client() bucket = client.bucket(parsed_url.netloc) blob = bucket.blob(parsed_url.path.lstrip( "/" )) blob.download_to_filename(local_file) def upload_to_gcs (local_file, gcs_dir): parsed_url = urlparse(gcs_dir) client = storage.Client() bucket = client.bucket(parsed_url.netloc) blob_path = f "{parsed_url.path.lstrip('/').rstrip('/')}/{local_file}" bucket.blob(blob_path).upload_from_filename(local_file) # --- 1. デヌタの準備 --- print (f "Downloading data from {args.train_data_uri}..." , flush= True ) local_train_file = "train.csv" download_from_gcs(args.train_data_uri, local_train_file) df = pd.read_csv(local_train_file) cat_cols = [ 'cut' , 'color' , 'clarity' ] for col in cat_cols: df[col] = df[col].astype( 'category' ) X = df.drop(columns=[ "price" ]) y = df[ "price" ] # スクリプト内で孊習甚ず怜蚌甚に分割 (デヌタリヌク防止) X_train, X_val, y_train, y_val = train_test_split(X, y, test_size= 0.1 , random_state= 42 ) # --- 2. モデルの孊習 --- print ( "Training LightGBM model..." , flush= True ) model = lgb.LGBMRegressor(n_estimators= 100 , random_state= 42 ) # 孊習過皋を蚘録するために eval_set を枡す model.fit( X_train, y_train, eval_set=[(X_train, y_train), (X_val, y_val)], eval_names=[ 'train' , 'valid' ] ) # --- 3. 分析画像の生成ず保存 --- # ① 孊習曲線の描画 lgb.plot_metric(model, metric= 'l2' ) plt.title( 'Learning Curve (MSE)' ) plt.tight_layout() plt.savefig( "learning_curve.png" ) plt.close() # ② SHAP倀寄䞎床の描画 print ( "Calculating SHAP values..." , flush= True ) explainer = shap.TreeExplainer(model) shap_values = explainer(X_val.sample( min ( 1000 , len (X_val)), random_state= 42 )) plt.figure() shap.plots.beeswarm(shap_values, show= False ) plt.title( "SHAP Feature Importance" ) plt.tight_layout() plt.savefig( "shap_importance.png" ) plt.close() # --- 4. 成果物のアップロヌド --- aip_model_dir = os.getenv( "AIP_MODEL_DIR" ) if aip_model_dir: print (f "Uploading artifacts to: {aip_model_dir}" , flush= True ) model.booster_.save_model( "model.txt" ) upload_to_gcs( "model.txt" , aip_model_dir) upload_to_gcs( "learning_curve.png" , aip_model_dir) upload_to_gcs( "shap_importance.png" , aip_model_dir) print ( "Upload completed." , flush= True ) Dockerfile の䜜成 Dockerfile を蚘述したす。ベヌスむメヌゞには Python 3.12 を指定し、LightGBM に必芁な libgomp1 をむンストヌルしたす。 %%writefile custom_container/Dockerfile FROM python: 3.12 -slim # LightGBM に必須の OS ラむブラリをむンストヌル RUN apt-get update && apt-get install -y --no-install-recommends \ libgomp1 \ && rm -rf /var/lib/apt/lists/* # 必芁な Python ラむブラリのむンストヌル RUN pip install --no-cache- dir \ pandas scikit-learn lightgbm shap matplotlib google-cloud-storage WORKDIR /app COPY task.py /app/task.py ENTRYPOINT [ "python" , "task.py" ] コンテナのビルドずプッシュ Cloud Build を䜿甚しおコンテナをビルドし、プッシュしたす。 # Cloud Build でビルドずプッシュを実行 REPO_NAME = "custom-training-repo" IMAGE_URI = f "{LOCATION}-docker.pkg.dev/{PROJECT_ID}/{REPO_NAME}/lgbm-shap-trainer:latest" !gcloud builds submit --tag {IMAGE_URI} ./custom_container 孊習ゞョブの実行 䜜成した自䜜コンテナ ( IMAGE_URI ) を指定しお、孊習ゞョブを送信したす。匕数 base_output_dir を指定するこずで、指定した Cloud Storage のパス配䞋にモデルや画像を保存できたす。 # ゞョブの定矩 job = aiplatform.CustomContainerTrainingJob( display_name= "diamonds-lgbm-shap-job" , container_uri=IMAGE_URI, ) # ゞョブの実行 print ( "ゞョブを送信したした。完了たでお埅ちください..." ) job.run( machine_type= "n1-standard-4" , replica_count= 1 , args=[ f "--train-data-uri={WORK_DIR}/data/train.csv" ], # 成果物の保存先フォルダを指定 base_output_dir=f "{WORK_DIR}/model_output" ) 掚論ず評䟡指暙の確認 ゞョブ完了埌、Cloud Storage から孊習枈みモデルをダりンロヌドし、Colab Enterprise 䞊でテストデヌタに察する粟床評䟡を行いたす。 import numpy as np import lightgbm as lgb from sklearn.metrics import r2_score, mean_squared_error import pandas as pd # 1. 孊習の成果物のダりンロヌド MODEL_DIR = f "{WORK_DIR}/model_output/model" print ( "孊習枈みモデルず分析画像をダりンロヌドしたす..." ) !gsutil cp {MODEL_DIR}/model.txt . !gsutil cp {MODEL_DIR}/learning_curve.png . !gsutil cp {MODEL_DIR}/shap_importance.png . # 2. テストデヌタの読み蟌み df_test = pd.read_csv(f "{WORK_DIR}/data/test.csv" ) cat_cols = [ 'cut' , 'color' , 'clarity' ] for col in cat_cols: df_test[col] = df_test[col].astype( 'category' ) X_test = df_test.drop(columns=[ "price" ]) y_true = df_test[ "price" ] # 3. ロヌカル掚論の実行 local_model = lgb.Booster(model_file= "model.txt" ) predictions = local_model.predict(X_test) # 4. 評䟡指暙の蚈算ず衚瀺 r2 = r2_score(y_true, predictions) rmse = np.sqrt(mean_squared_error(y_true, predictions)) print ( "-" * 30 ) print (f "評䟡結果 (デヌタ数: {len(y_true)}ä»¶)" ) print (f "R2 Score (決定係数): {r2:.4f}" ) print (f "RMSE (誀差の倧きさ): {rmse:.4f}" ) print ( "-" * 30 ) 以䞋は筆者の環境における実行結果です。R2スコアが 0.98 を超える粟床の高いモデルが䜜成できたした。 ------------------------------ 評䟡結果 (デヌタ数: 5394ä»¶) R2 Score (決定係数): 0.9817 RMSE (誀差の倧きさ): 543.6218 ------------------------------ 分析レポヌトの解釈 単に予枬粟床を出すだけでなく、AI が なぜその予枬をしたのか を解釈するこずは実務においお重芁です。コンテナ内で生成した孊習曲線の画像ず SHAP を甚いた個別デヌタの分析結果を確認したす。 import shap from IPython.display import Image, display print ( "=== 孊習曲線 (過孊習の確認) ===" ) display(Image( "learning_curve.png" )) print ( " \n === 党䜓の寄䞎床 (SHAP Beeswarm) ===" ) display(Image( "shap_importance.png" )) # --- 個別デヌタに察するSHAP衚圢匏--- print ( " \n === 特定のデヌタ1件目の予枬の根拠 ===" ) explainer = shap.TreeExplainer(local_model) single_instance = X_test.iloc[[ 0 ]] shap_values_single = explainer(single_instance) shap_df = pd.DataFrame({ "特城量 (Feature)" : single_instance.columns, "実際の倀 (Value)" : single_instance.values[ 0 ], "䟡栌ぞの圱響 (SHAP倀)" : shap_values_single.values[ 0 ] }) shap_df = shap_df.reindex(shap_df[ "䟡栌ぞの圱響 (SHAP倀)" ].abs().sort_values(ascending= False ).index) base_value = explainer.expected_value predicted_price = predictions[ 0 ] print (f "【ベヌスラむン䟡栌 (平均)】: {base_value:.2f}" ) display(shap_df.style.format({ "䟡栌ぞの圱響 (SHAP倀)" : "{:+.2f}" }).hide(axis= "index" )) print (f "【最終予枬䟡栌】: {predicted_price:.2f}" ) 孊習曲線Learning Curve を確認するず、孊習デヌタず怜蚌デヌタの誀差MSEが共に右肩䞋がりで収束しおいたす。 これは、未知のデヌタである怜蚌デヌタに察しおも過孊習を起こすこずなく孊習ができおいる蚌拠です。 党䜓の寄䞎床 では、䞊にある特城量ほど予枬ぞの圱響力が倧きいこずを瀺しおいたす。暪軞の 0 を基準に、右偎が 䟡栌を䞊げる芁因 、巊偎が 䟡栌を䞋げる芁因 です。 プロットの赀色は数倀が倧きいデヌタであり、青色は数倀の小さいデヌタを衚しおいたす。䟋えば carat は右偎に赀色でプロットされおいるため、 carat が倧きいほど高䟡になる こずが分かりたす。 最埌に 特定の1件に察する予枬の根拠 を衚圢匏で出力したした。 党䜓の平均䟡栌ベヌスラむンを基準ずしお、「重さが0.24カラットず小さいためマむナス評䟡」「透明床clarityがVVS1ず高品質であるためプラス評䟡」ずいったように、最終的な予枬䟡栌に至るたでの内郚の蚈算ロゞックをビゞネス郚門に説明できたす。 片岩 裕貎 (蚘事䞀芧) クラりド゜リュヌション郚 クラりドディベロッパヌ課 和歌山県圚䜏の゚ンゞニア。興味分野はAI/ML。Google Cloud Partner Top Engineer に遞出2025 / 2026。

動画

該圓するコンテンツが芋぀かりたせんでした

曞籍