Open GENZITSU opened 1 year ago
範囲選択箇所のコードレビューやリファクタリング、コードの説明などをしてくれる拡張機能の利用方法を解説している記事
無料分のクレジットが付与されるとはいえ、優良ではあるので使用感はわからず。 OpenAPI側に入力が送られるので利用には慎重にならないと行けなさそう。
編集距離計算でよく用いられるlevenshteinよりも高速なライブラリRapidFuzzの紹介
RapidFuzzでは文字列の配列同士での距離比較や文字列比較前に実行する前処理のカスタマイズなどが可能でN:Nの他にも1:N, 1:1の比較も可能となっている。
以下カバーされている距離関数
- DamerauLevenshtein : ダメラウ・レーベンシュタイン距離
- Hamming : ハミング距離
- Indel : インデル距離
- Jaro : ジャロ距離
- JaroWinkler : ジャロ・ウィンクラー距離
- Levenshtein : レーベンシュタイン距離
- Optimal String Alignment (OSA) : OSA距離
- Prefix : 接頭辞距離
- Postfix : 接尾辞距離
デフォルトの前処理関数も用意されていて、reよりも高速とのこと
rapidfuzz.utils.default_processという前処理関数が提供されています。文字列を入力すると
- 英数字のみを抽出(記号を除去)
- 先頭と末尾のスペースを削除
- 小文字化
- を行います。 re.sub()より5倍以上も高速 です。
とても便利そう
非常に大量のサンプルに対するMLソリューションの実装・運用におけるtipsを紹介している記事
学習時のメモリ節約方法や推論時間の高速化、作成したモデルの管理方法など勉強になることが多い
予測ファネルでは、ユーザーの行動ログ、興味関心、属性などのデータから、ユーザーごとにCVする可能性を予測したスコアを活用して配信します。 スコア化した結果をマーケティングファネルに落とし込むことでCV見込みユーザーのボリュームを可視化し、高スコアユーザーに広告配信することでより多くのCVを獲得することが可能になります。 また、ミドルスコアユーザーに広告配信することで商材に関する興味関心の意識を醸成し、将来的にCVするユーザーを育成することも可能に
特徴量として用いる行動ログデータのlibsvm形式への変換 libsvm形式へ変換するためにAWS上で、もともと横持ちのデータとしてS3に格納しているデータをlibsvm形式に変換する前処理を実施しています。
sklearnでも利用可能なのがえらい
scikit-learnライブラリのload_svmlight_filesを使用してlibsvm形式のファイルを読み込めば、以降は通常のscikit-learnに搭載されたモデルを学習するのと同様の手順でモデル作成が可能です。
大体1/3くらいになるっぽい
libsvm形式にすることでファイル容量をlibsvm形式変換前の3分の1以下に抑え、モデルの学習時に必要なメモリリソースも大幅に抑制することができました。
学習用のlibsvm形式と推論用のデータ形式の二つを随時作成する日次バッチ処理が組まれていて、モデル作成リクエストに従って該当のデータを取得し、モデルを作るという構成
ロジスティック回帰モデルを採用し、かつ推論をAthena上でSQL処理させることで高速推論を実現 推論に時間のかかる決定木モデルなどは使わず、推論の速いロジスティック回帰を採用し、さらにAthena上で推論をSQL処理することで高速な推論を可能にしました
メリットはかなり大きく
C推論時間をおよそ1分に短縮することができました(1分は5000万ユーザーに推論した場合の時間)。同じユーザー数に対しするPython上でscikit-learnのRandom Forestモデルを用いて推論する場合は2時間以上
SQLで推論できるため、データを保持することなく推論することが可能になり、データのIOを簡素化
CVのスコアを活用する方法としてもとても勉強になった ロジスティック回帰でも商用に耐えるようにするための特徴量設計が以外と大変そうだが、SQL処理できると言うメリットはこの規模だととても大きそう 1つのモデルの運用であればバッチ処理でよいが、複数ユーザーから複数モデルの作成・予測が求められるケースだとこっちの方に軍配が上がるか
tf2kerasで画像認識モデルの実装と学習済みモデルの配布がされているOSS
実装されているモデルはtimmとあまり差がないが、tfで実装されているのでtflie形式にできたりTPUを使いやすいのが利点だろうか
databricks上で画像系DNNモデルを分散学習する際に必要となる前準備とDataLoaderの書き方をコードとともに紹介している記事
具体的には画像をsparkdfで読み取る際にラベルとバイナリの形式でデータを読み一度parquetに変換し、parquet形式を読み込めるようにSparkDatasetConverterのmake_torch_dataloader()メソッドを利用するとのこと
databricks上で学習すると言うのはなかなかレアそうだが、参考になる。
inpatingやmatting, super resolution などの画像編集処理を行う各種モデルが実装されているOSS
open-mmlabがカバーしている範囲が意外と広いことに驚くばかり
その他で気になったレポジトリ
pytorchのデータローダをmulti worker, multi gpuで利用した際にメモリが大量に消費されてしまう事象の原因と対処法を解説しているブログ
Linuxがサブプロセスを生成する際にCopy-on-writeが走り、親プロセスが保持しているメモリ領域を子プロセスがまるごとコピーしてしまうため。
processがspwanされる際にメモリーをコピーせずに親プロセスの領域を3章できるように、プロセス間で共有したいデータはtorch.Tensor形式でシリアライズしておく
# from https://github.com/ppwwyyxx/RAM-multiprocess-dataloader/blob/200f5f2a209c2ce624b35e54746e07fbdb04ebda/serialize.py#L40
class TorchSerializedList(NumpySerializedList):
def __init__(self, lst: list):
super().__init__(lst)
self._addr = torch.from_numpy(self._addr)
self._lst = torch.from_numpy(self._lst)
def __getitem__(self, idx):
start_addr = 0 if idx == 0 else self._addr[idx - 1].item()
end_addr = self._addr[idx].item()
bytes = memoryview(self._lst[start_addr:end_addr].numpy())
return pickle.loads(bytes)
以下のようにメモリ使用量を削減できる
ここからさらにプロセスごとのtorchのimport分を削減したい場合は以下のようなテクニックがある
multiprocessing.set_forkserver_preload(["torch"])
さらにmulti gpu x multi workerに対応させるためには以下のように、GPU番号に応じた処理を追加する必要ががある。
# from https://github.com/ppwwyyxx/RAM-multiprocess-dataloader/blob/200f5f2a209c2ce624b35e54746e07fbdb04ebda/serialize.py#L55
# NOTE: https://github.com/facebookresearch/mobile-vision/pull/120
# has another implementation that does not use tensors.
class TorchShmSerializedList(TorchSerializedList):
def __init__(self, lst: list):
from detectron2.utils import comm
if comm.get_local_rank() == 0:
super().__init__(lst)
if comm.get_local_size() == 1:
# Just one GPU on this machine. Do nothing.
return
if comm.get_local_rank() == 0:
# Move to shared memory, obtain a handle.
serialized = bytes(mp.reduction.ForkingPickler.dumps(
(self._addr, self._lst)))
# Broadcast the handle of shared memory to other GPU workers.
comm.all_gather(serialized)
else:
serialized = comm.all_gather(None)[comm.get_rank() - comm.get_local_rank()]
# Materialize a tensor from shared memory.
self._addr, self._lst = mp.reduction.ForkingPickler.loads(serialized)
print(f"Worker {comm.get_rank()} obtains a dataset of length="
f"{len(self)} from its local leader.")
worker数を増やした際にべらぼうにメモリを消費してしまう事象に悩まされていたが、これで解決しそう
MultilabelStratifiedKFoldの実装をしてくれているOSS
# from https://github.com/trent-b/iterative-stratification
from iterstrat.ml_stratifiers import MultilabelStratifiedKFold
import numpy as np
X = np.array([[1,2], [3,4], [1,2], [3,4], [1,2], [3,4], [1,2], [3,4]])
y = np.array([[0,0], [0,0], [0,1], [0,1], [1,1], [1,1], [1,0], [1,0]])
mskf = MultilabelStratifiedKFold(n_splits=2, shuffle=True, random_state=0)
for train_index, test_index in mskf.split(X, y):
print("TRAIN:", train_index, "TEST:", test_index)
X_train, X_test = X[train_index], X[test_index]
y_train, y_test = y[train_index], y[test_index]
メモ
GroupedMultilabelStratifiedKFoldの実装
# from https://www.kaggle.com/code/jakubwasikowski/stratified-group-k-fold-cross-validation/notebook
def stratified_group_k_fold(X, y, groups, k, seed=None):
labels_num = np.max(y) + 1
y_counts_per_group = defaultdict(lambda: np.zeros(labels_num))
y_distr = Counter()
for label, g in zip(y, groups):
y_counts_per_group[g][label] += 1
y_distr[label] += 1
y_counts_per_fold = defaultdict(lambda: np.zeros(labels_num))
groups_per_fold = defaultdict(set)
def eval_y_counts_per_fold(y_counts, fold):
y_counts_per_fold[fold] += y_counts
std_per_label = []
for label in range(labels_num):
label_std = np.std([y_counts_per_fold[i][label] / y_distr[label] for i in range(k)])
std_per_label.append(label_std)
y_counts_per_fold[fold] -= y_counts
return np.mean(std_per_label)
groups_and_y_counts = list(y_counts_per_group.items())
random.Random(seed).shuffle(groups_and_y_counts)
for g, y_counts in sorted(groups_and_y_counts, key=lambda x: -np.std(x[1])):
best_fold = None
min_eval = None
for i in range(k):
fold_eval = eval_y_counts_per_fold(y_counts, i)
if min_eval is None or fold_eval < min_eval:
min_eval = fold_eval
best_fold = i
y_counts_per_fold[best_fold] += y_counts
groups_per_fold[best_fold].add(g)
all_groups = set(groups)
for i in range(k):
train_groups = all_groups - groups_per_fold[i]
test_groups = groups_per_fold[i]
train_indices = [i for i, g in enumerate(groups) if g in train_groups]
test_indices = [i for i, g in enumerate(groups) if g in test_groups]
yield train_indices, test_indices
メモ
Juman++ V2のインストール手順
表題の通り
コメント
sudoなしで実行できるのが嬉しいが、Juman++はそのほかのtokenzierと比べて実行速度がなぁ...
出典
Juman++ V2のインストール手順