CoreML変換で入力を正規化する方法まとめ（inputにmean stdを使う）

# CoreMLの入力正規化ってどうすればいいの？

一般的に機械学習モデルで画像入力を使う場合、入力のピクセル値は小さく正規化されます。
具体的には、元々画像のピクセル値は RGBそれぞれ
**0~255**
で表現されますが、機械学習モデルの入力として使うときは
**0~1**
や
**-1~1**
などの小さな値に変換されます。

**0~1**　であればpythonでは

```python
# original python code
input = img / 255
```

**-1~1**　であればpythonでは

```python
# original python code
input = img / 127.5 -1
```

のように計算されています。

また、**mean（平均）を引いてstd(標準偏差)で割る**という正規化されることもあります。
以下のようなImageNetのmean（平均）とstd(標準偏差)がよく用いられます。

```python
# 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 正規化の場合

```python
import coremltools as ct
mlmodel = ct.convert(traced_model, inputs=[ct.ImageType(shape=input.shape,scale=1/255)])
```

### -1~1 正規化の場合

```python
import coremltools as ct
mlmodel = ct.convert(traced_model, inputs=[ct.ImageType(shape=input.shape,scale=1/127.5,bias=[-1,-1,-1])])
```

### 平均と標準偏差を用いた正規化の場合（ImageNet）

```python
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と似た感じで前処理を設定できます。

https://qiita.com/john-rocky/items/b9f08b5e213734bd89c8

### 0~1 正規化の場合

```Swift
let preProcessedMlMultiArray:MLMultiArray = uiImage.mlMultiArray(scale: 1/255)
```

### -1~1 正規化の場合

```Swift
let preProcessedMlMultiArray:MLMultiArray = uiImage.mlMultiArray(scale: 1/127.5, rBias: -1, gBias: -1, bBias: -1)
```

### 平均と標準偏差を用いた正規化の場合（ImageNet）

```Swift
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に戻す）以下のような処理を行っている場合

```python
# 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になっていることが多いと僕は思います）

### 変換時に非正規化を追加する

```python
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などに変える際に非正規化することもできます。

https://github.com/hollance/CoreMLHelpers

```swift
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関連の情報を発信しています。

[Twitter](https://twitter.com/JackdeS11)
[Medium](https://medium.com/@rockyshikoku)
[GitHub](https://github.com/john-rocky)