CoreMLの入力正規化ってどうすればいいの?
一般的に機械学習モデルで画像入力を使う場合、入力のピクセル値は小さく正規化されます。
具体的には、元々画像のピクセル値は RGBそれぞれ
0~255
で表現されますが、機械学習モデルの入力として使うときは
0~1
や
-1~1
などの小さな値に変換されます。
0~1 であればpythonでは
# original python code
input = img / 255
-1~1 であればpythonでは
# original python code
input = img / 127.5 -1
のように計算されています。
また、mean(平均)を引いてstd(標準偏差)で割るという正規化されることもあります。
以下のようなImageNetのmean(平均)とstd(標準偏差)がよく用いられます。
# original python code
mean = [0.406, 0.456, 0.485] #BGR
std = [0.225, 0.224, 0.229]
input = img.astype(np.float32) / 255.
input -= mean
input /= std
これらの入力をCoreMLで表現するにはどうすればいいのでしょうか?
変換時に設定する
CoreMLToolsの変換メソッドにはscaleとbiasという引数があり、そこで正規化を設定できます。
これにより上記のpythonによる正規化計算と同等の計算をRGBそれぞれのピクセル値に行えます。
0~1 正規化の場合
import coremltools as ct
mlmodel = ct.convert(traced_model, inputs=[ct.ImageType(shape=input.shape,scale=1/255)])
-1~1 正規化の場合
import coremltools as ct
mlmodel = ct.convert(traced_model, inputs=[ct.ImageType(shape=input.shape,scale=1/127.5,bias=[-1,-1,-1])])
平均と標準偏差を用いた正規化の場合(ImageNet)
import coremltools as ct
mlmodel = ct.convert(traced_model, inputs=[ct.ImageType(shape=input.shape,bias=[-0.485/0.229,-0.456/0.224,-0.406/0.225],scale=1.0/255.0/0.226)])
CoreMLToolsのImageTypeを使う場合は上記の設定でCoreMLモデルが正規化を行なってくれます。
TensorType入力をSwiftで正規化する場合
何らかの事情でCoreMLのImageType入力を使わずTensorType入力に正規化を行いたい場合、
変換時には正規化の前処理を設定せず、SwiftでMLMultiArrayの操作を行います。
UIImageをMLMultiArrayに変換する以下のコードスニペット(僕著)でCoreMLToolsと似た感じで前処理を設定できます。
0~1 正規化の場合
let preProcessedMlMultiArray:MLMultiArray = uiImage.mlMultiArray(scale: 1/255)
-1~1 正規化の場合
let preProcessedMlMultiArray:MLMultiArray = uiImage.mlMultiArray(scale: 1/127.5, rBias: -1, gBias: -1, bBias: -1)
平均と標準偏差を用いた正規化の場合(ImageNet)
let x_1 = image_masked?.mlMultiArray(scale: 1/255.0/0.226,rBias: -0.485/0.229,gBias: -0.456/0.224,bBias: -0.406/0.225)
出力を非正規化する
もしも元のpythonコードが出力を非正規化する(正規化した小さな値から0~255に戻す)以下のような処理を行っている場合
# original python code
output = model_output * 255 # 0~1 to 0~255
# output = (model_output+1) * 127.5 # -1~1 to 0~255
CoreMLの出力も非正規化する必要があります。
(平均と標準偏差を用いた入力正規化の場合も、出力は0~1か-1~1になっていることが多いと僕は思います)
変換時に非正規化を追加する
activation_params = [255,0] # 0~1から0~255に非正規化している場合
activation_params = [127.5,127.5] # -1~1から0~255に非正規化している場合
import coremltools as ct
from coremltools.proto import FeatureTypes_pb2 as ft
from coremltools.models.utils import save_spec
mlmodel = ct.models.MLModel('your_model.mlmodel')
spec = mlmodel.get_spec()
original_model_output_name = spec.description.output.name
builder = ct.models.neural_network.NeuralNetworkBuilder(spec=spec)
builder.add_squeeze(name="squeeze", input_name=original_model_output_name, output_name="squeeze_out", axes = None, squeeze_all = True)
builder.add_activation(name="activation",non_linearity="LINEAR",input_name="squeeze_out",output_name="activation_out",params=activation_params)
builder.spec.description.output.pop()
builder.spec.description.output.add()
output = builder.spec.description.output[0]
output.name = "activation_out"
output.type.imageType.colorSpace = ft.ImageFeatureType.ColorSpace.Value('RGB')
output.type.imageType.width = {your_model_output_width}
output.type.imageType.height = {your_model_output_height}
save_spec(builder.spec, 'yourModel.mlmodel')
CoreMLHelpersで画像出力を得る際に非正規化する
あるいは、MLMultiArrayの出力のままでCoreMLHelpersを用いてCGImageなどに変える際に非正規化することもできます。
let out = try model.prediction(from: input)
let outArray = out.featureValue(for: "var_2132")?.multiArrayValue
let outImage = outArray?.cgImage(min: 0,max: 1, axes: (1,2,3)) // 0~1 to 0~255
// let outImage = outArray?.cgImage(min: -1,max: 1, axes: (1,2,3)) // -1~1 to 0~255
Happy Machine Learning!
🐣
フリーランスエンジニアです。
お仕事のご相談こちらまで
rockyshikoku@gmail.com
機械学習、ARアプリ(Web/iOS)を作っています。
機械学習/AR関連の情報を発信しています。