こんにちは、電通総研、Xイノベーション本部 AIトランスフォーメーションセンター所属の徳原光です。
以前、AndroidスマホでAIモデルを運用するために、Pandasの特徴量計算のコードをKotlin DataFrameを使用してKotlinに移植するための変換表を公開しました。
今度は、特徴量計算のコードをSwiftに移植するため、pandasとTabularDataの変換表を作成しました。
Pandas ⇔ TabularData変換表
自分がよく使用していた処理から(Pandas的に)基本的なものを抜粋しています。無理やり表にしているので改行が変ですがご容赦ください。
処理 | pandas | TaburalData | ドキュメント |
---|---|---|---|
データフレーム生成 | df = pd.DataFrame({ "A": [2, 4, 6, 8, 10], "B": [3, 6, 9, 12, 15], "C": [5, 10, 15, 20, 25], "G": ["a", "b", "c", "a", "b"] }) | var df = DataFrame(dictionaryLiteral: ("A", [2, 4, 6, 8, 10]), ("B", [3.0, 6.0, 9.0, 12.0, 15.0]), ("C", [5, 10, 15, 20, 25]), ("G", ["a", "b", "c", "a", "b"])) | ? |
データフレーム読み込み | df = pd.read_csv('hogehoge.csv') | let fileURL = Bundle.main.url(forResource: "hogehoge", withExtension: "csv") else { fatalError("File not found")}do { let df = try DataFrame(contentsOfCSVFile: fileURL, options: options) print("\(df)")} catch { print("Error loading CSV: \(error)")} | ? |
冒頭表示 | df.head() | df.prefix(5) | 公式ドキュメント |
データフレームを縦に結合 | pd.concat([df1, df2], axis=0) | df1.append(df2) | ? |
列を取得 | df[”A”] | df[”A”] | ? |
行を取得 | df.loc[0] | df.rows[0]df.rows[2...5] | 公式ドキュメント |
行数を取得 | len(df) | df.rows.count | 公式ドキュメント |
条件にあうデータを抽出 | df.loc[df["G"] == "a", :] | df.filter(on: "G", String.self, { $0 == "a" }) | ? |
カラムの値を更新 | df[”A”] = df[”A”] - 2 | df["A"] = df["A"].map{ $0! - 2 } | ? |
カラム名のリストに該当するカラムを選択 | list_col = ["A", "B"]df[list_col] | df[ [ "A", "B" ] ] | ? |
カラム追加 | df['D'] = 1df['D'] = df['C'] - df['B'] | df.append(column: Column(name: "Integers", contents: [1, 1, 1, 1, 1])) | ? |
カラムを削除 | df.drop("A") | df.removeColumn("A") | ? |
カラム名をリストで取得 | df.columns | print(df.columns.map { $0.name }) | ? |
カラム内の値をリストで取得 | df[”A”].to_list() |
df[”A”].map { $0 as! Double } df[”A”].map { $0 as! Int} |
? |
カラム名を変更 | df.rename(columns={'A': 'K'}) |
df2.append(column: Column(name:"K", contents: df["A"].map { $0 as! Int})) «df2.removeColumn("A") |
? |
列の値を編集 | df[”A”] = df[”A”] - 5 | df[”A”] = df["A"].map{ $0! - 5 } | ? |
集計 | df.groupby('G').agg(["count", "sum", "mean" ]) |
df.grouped(by: "G").sums("B", Double.self, order: .descending) df.grouped(by: "G").means("B", Double.self, order: .descending) |
公式ドキュメント |
外部結合 | result = pd.merge(df1, df2, on='D', how='inner') |
df2.joined(df3, on: ("A"))
df2.joined(df3, on: ("A")) |
? |
TabularDataの使用感
SwiftのTabularDataでAIモデル運用のための特徴量計算を実装した感想は、当たり前ですが、Python×Pandasと同じような使用感で実装を進めることはできませんでした。
やはりPython環境ではPandasだけではなくnumpyやmatplotlibといったライブラリも提供されているので、単純なPandasとTabularDataの機能差だけではなく、データサイエンスや単純な数値計算に関連するライブラリの充実度やネットに公開されている情報量の差によって思うようにSwift移植が進まない場面が多かったです。
学習データとなるネットの情報がすくないので当然ですが、生成AIによるコード生成もあまり有効ではありませんでした・・・。
Kotlin DataFrameと比較してどうだったかというと、Kotlin DataFrameと比較してもかなり苦戦しましたね。
Python→Kotlin→Swiftと実装していたので、だんだん難易度が上がって情報不足に慣れながら実装できたので良かったですが、TabularDataはKotlin DataFrameと比較して公式が出している情報量も少なく、さらにGithub上で公開されているTabularDataを使ったソースコードの量も少なかったです。
また、Kotlinでの実装はJavaの豊富なライブラリを使用できることができますが、Swiftではそうはいかず、IoT機器としてAIモデルを運用するとしたら、AndroidよりiOSのほうが難易度が高いと感じました。
AndroidとIOSでの機械学習モデルの運用
ちなみにモデルの運用は、AndroidではONNX Runtime、iOSではCore MLを使用しました。モデルのメタ情報についてXcodeのGUIで確認できる、運用のためのクラスが自動で生成されるという点でCoreMLのほうが使用感は良かったですね。
Pythonで提供されている専用のモジュールでモデルを出力し、そのデータをXcode上でプロジェクトの適当なグループに追加すれば、あとはモデル名と同名のクラスを呼びだすだけで使えてかなり工数を節約できました。
ただ、Flutterプロジェクトで使用するとプロジェクト開発言語をObjective-CからSwiftに明示的に設定を変えないと動かない問題や、対応している機械学習モデルが少ない問題(LightGBMが使えない)、対応していてもモデルの提供ライブラリのバージョンを落とさないと使えない問題がCoreMLにはありました。
TabularDataを使用するために確認しておいたほうがいい情報
developer.apple.comでは、TabularDataの紹介動画が公開されています。TabularDataを使って住所情報を分析するデモンストレーションが行われているので、こちらは必ず確認したほうが良いと思います。
あと、こちらの記事はサンプルコードが載っているのでよく見ていました。
いかのサイトに今回の変換表を作るにあたって使用したサンプルコードをおいています。
とにかく情報不足に陥るので、活用できる情報は一通り見ておくことをお勧めします。とりあえず、今回のAIモデルをスマホで運用するという1件を通して実装スキルが向上したなと感じました。
ちなみに、実装後のアプリの挙動はどうだったかというと、スマホのスペック不足を心配していましたが快適です。やはり最近のモバイル端末は一昔前のPC並に高いので、定番のモデルでしたら運用できますね。
最後に、私が所属しているAIトランスフォーメーションセンターでは、一緒に働いてくださる方を募集しております。こちらのページに採用に関する内容がまとめられております。また、カジュアル面談の募集もこちらのページからできますので、是非ご覧ください。
それでは。
執筆:@tokuhara.hikaru、レビュー:@yamashita.tsuyoshi
(Shodoで執筆されました)