記事とコードは [Github ウェアハウス: https://github.com/timerring/dive-into-AI ]にアーカイブされています。または、パブリック アカウント [AIShareLab] は、pytorch チュートリアルに返信することで取得することもできます。
記事ディレクトリ
torchvision.transforms.Pad
torchvision.transforms.Pad(padding, fill=0, padding_mode='constant')
機能: 画像の端を埋める
- パディング: パディングサイズを設定します
- aの場合、ピクセルは上下左右に塗りつぶされます。
- (a, b)の場合、左右にaピクセルを塗りつぶし、上下にbピクセルを塗りつぶします。
- (a, b, c, d)の場合、左上と右下にそれぞれa、b、c、dを記入します
- padding_mode: パディング モード。定数、エッジ、リフレクト、対称の 4 つのモードがあります。
- fill:padding_mode が定数の場合、塗りつぶされたピクセル値 (R、G、B) または (Gray) を設定します。
torchvision.transforms.ColorJitter
torchvision.transforms.ColorJitter(brightness=0, contrast=0, saturation=0, hue=0)
機能: 明るさ、コントラスト、彩度、色相を調整します。写真撮影の過程では、機材や照明の問題により色のずれが生じる場合があるため、これらの要因によって引き起こされる乱れを相殺するためにこれらの属性を調整する必要があります。
- 明るさ: 明るさ調整係数
- コントラスト:コントラストパラメータ
- 彩度:彩度パラメータ
- 明るさ、コントラスト、彩度パラメータ:
- aの場合は[max(0, 1-a), 1+a]からランダムに選択します。
- (a,b)の場合は[a,b]から選択します。
- 色相: 色相パラメータ
- aの場合、パラメータを[-a,a]から選択します。ここで、0 ≤ a ≤ 0.5 0\le a \le 0.50≤ある≤0.5。
- (a,b)の場合、[a,b]からパラメータを選択します。ここで、0 ≤ a ≤ b ≤ 0.5 0 \le a \le b \le 0.50≤ある≤b≤0.5。
変換.グレースケール(ランダムグレースケール)
torchvision.transforms.Grayscale(num_output_channels=1)
機能: 画像をグレースケール画像に変換します
- num_output_channels: 出力チャンネルの数。1 または 3 にのみ設定できます ( 3 チャンネルからの入力しか受信できない
transforms.Normalize
ため、後で使用する場合は 3 に設定する必要があります)transforms.Normalize
- グレースケールは RandomGrayscale の特殊なケース、つまり p = 1 の特殊なケースです。
torchvision.transforms.RandomGrayscale(p=0.1, num_output_channels=1)
- p: 確率値、画像がグレースケール画像に変換される確率
- num_output_channels: 出力チャンネルの数。1 または 3 にのみ設定できます
機能: 特定の確率に従って画像をグレースケール画像に変換します。
変換.RandomAffine
torchvision.transforms.RandomAffine(degrees, translate=None, scale=None, shear=None, resample=False, fillcolor=0)
機能: 画像にアフィン変換を実行します アフィン変換は 2 次元の線形変換であり、回転、平行移動、拡大縮小、ずらし、反転という 5 つの基本操作で構成されます。
- 度: 回転角度の設定
- translation: 変換間隔の設定 (a、b) など。a は幅 (width) を設定し、b は高さ (height) を設定します。ワイド次元での画像変換の間隔は、− imgwidth × a < dx < imgwidth × a - img_{width} \times a < dx < img_{width} \times a−画像_w i d th _×ある<dx _<私はw i d th _×a、同様に高い。
- スケール: スケール、エリア内
- fillcolor: 塗りつぶしの色の設定
- shear: シアー角度の設定。水平シアーと垂直シアーがあります。ミスカットの意味:例えば、絵のx軸を平行に保ち、絵のy軸を斜めに引っ張って絵全体が平行四辺形になるような横方向のサイドカット(x軸サイドカット)です。
- a の場合、x 軸のみをミスカットし (x 軸を平行に保ちます)、(-a, a) の間のミスカット角度をランダムに選択します。
- (a, b) の場合、x 軸は (-a, a) の間のミスカット角度をランダムに選択し、y 軸は (-b, b) の間のミスカット角度をランダムに選択します。
- (a, b, c, d) の場合、x 軸は (a, b) 間のミスカット角度をランダムに選択し、y 軸は (c, d) 間のミスカット角度をランダムに選択します。
- resample: NEAREST、BILINEAR、BICUBIC などのリサンプリング方法。
変換.ランダム消去
torchvision.transforms.RandomErasing(p=0.5, scale=(0.02, 0.33), ratio=(0.3, 3.3), value=0, inplace=False)
上記のパラメータは、論文に記載されているより適切な範囲です。
機能: 画像をランダムに隠します。この演算への入力はテンソルです。したがって、その前に実行する必要がありますtransforms.ToTensor()
。同時に以下をコメントアウトしますtransforms.ToTensor()
。
- p: 確率値、操作が実行される確率
- スケール: オクルージョンされた領域の面積。(a, b) のように、(a, b) のオクルージョン率がランダムに選択されます
- 比率: オクルージョン領域のアスペクト比。(a, b) のように、(a, b) のアスペクト比がランダムに選択されます
- value: オクルージョンされた領域のピクセル値を設定します。(R、G、B)、グレー、または任意の文字列。前回の実行により
transforms.ToTensor()
、ピクセル値は 0 ~ 1 の間に正規化されているため、ここで設定した (R, G, B) を 255 で割る必要があります。
transforms.RandomErasing(p=1, scale=(0.02, 0.33), ratio=(0.3, 3.3), value=(254/255, 0, 0))
の効果は次のとおりですscale=(0.02, 0.33)
。オクルージョン領域の比率をランダムに選択ratio=(0.3, 3.3)
し、オクルージョン領域のアスペクト比をランダムに選択します。値で設定された RGB 値は 0 から 1 の間に正規化する必要があります。
transforms.RandomErasing(p=1, scale=(0.02, 0.33), ratio=(0.3, 3.3), value='timerring')
効果は次のとおりです。値が任意の文字列に設定されている場合、ランダムな値がオクルージョンされた領域を埋めるために使用されます。
変換.Lambda
transforms . Lambda ( lambd)
lambd: ラムダ匿名関数。
機能: 変換方法をカスタマイズします。
lambda [arg1 [arg2, ... , argn]] : expression
コロンの前は入力パラメータで、その後に処理された式が続きます。これは return の意味と似ています。
例えば、上記のような場面で
FiveCrop
使われていますtransforms.Lambda
。transforms.FiveCrop(112, vertical_flip=False), transforms.Lambda(lambda crops: torch.stack([(transforms.ToTensor()(crop)) for crop in crops]))
transforms.FiveCrop
戻り値は長さ 5 のタプルであるため、transforms.Lambda
テンソルを使用してタプルを 4D に変換する必要があります。
変換の組み合わせと選択
torchvision.transforms.RandomChoice
torchvision.transforms.RandomChoice([transforms1, transforms2, transforms3])
機能: 一連の変換メソッドからランダムに 1 つを選択します
変換.RandomApply
torchvision.transforms.RandomApply([transforms1, transforms2, transforms3], p=0.5)
機能:確率に従って一連の変換操作を実行します。すべてが実行されるか、まったく実行されません。
変換.RandomOrder
transforms.RandomOrder([transforms1, transforms2, transforms3])
一連の変換に対する操作の順序をシャッフルします。
カスタム変換
カスタム変換の 2 つの要素:
- 1 つのパラメータのみを受け入れ、1 つのパラメータを返します。
- 上流と下流の入力と出力に注意してください。前の変換の出力は次の変換の入力になります。
塩胡椒ノイズを実装します。インパルス ノイズとしても知られるソルト アンド ペッパー ノイズは、ランダムな白色点または黒色点であり、白色点はソルト ノイズ、黒色点はペッパー ノイズと呼ばれます。信号対ノイズ比 (信号対ノイズ率、SNR) は、ノイズの割合、つまり画像内のすべてのピクセルに対する通常のピクセルの割合の尺度です。
AddPepperNoise
塩胡椒ノイズを追加するための変換としてクラスを定義します。信号対雑音比と確率がコンストラクターに渡され、__call__()
関数内で特定のロジックが実行され、画像が返されます。
import numpy as np
import random
from PIL import Image
# 自定义添加椒盐噪声的 transform
class AddPepperNoise(object):
"""增加椒盐噪声
Args:
snr (float): Signal Noise Rate
p (float): 概率值,依概率执行该操作
"""
def __init__(self, snr, p=0.9):
assert isinstance(snr, float) or (isinstance(p, float))
self.snr = snr
self.p = p
# transform 会调用该方法
def __call__(self, img):
"""
Args:
img (PIL Image): PIL Image
Returns:
PIL Image: PIL image.
"""
# 如果随机概率小于 seld.p,则执行 transform
if random.uniform(0, 1) < self.p:
# 把 image 转为 array
img_ = np.array(img).copy()
# 获得 shape
h, w, c = img_.shape
# 信噪比
signal_pct = self.snr
# 椒盐噪声的比例 = 1 -信噪比
noise_pct = (1 - self.snr)
# 选择的值为 (0, 1, 2),每个取值的概率分别为 [signal_pct, noise_pct/2., noise_pct/2.]
# 椒噪声和盐噪声分别占 noise_pct 的一半
# 1 为盐噪声,2 为 椒噪声
mask = np.random.choice((0, 1, 2), size=(h, w, 1), p=[signal_pct, noise_pct/2., noise_pct/2.])
mask = np.repeat(mask, c, axis=2)
img_[mask == 1] = 255 # 盐噪声
img_[mask == 2] = 0 # 椒噪声
# 再转换为 image
return Image.fromarray(img_.astype('uint8')).convert('RGB')
# 如果随机概率大于 seld.p,则直接返回原图
else:
return img
あとは直接呼び出すだけですAddPepperNoise
。
完全なコードは次のとおりです。
# -*- coding: utf-8 -*-
import os
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
import numpy as np
import torch
import random
import torchvision.transforms as transforms
from PIL import Image
from matplotlib import pyplot as plt
from torch.utils.data import DataLoader
path_lenet = os.path.abspath(os.path.join(BASE_DIR, "..", "..", "model", "lenet.py"))
path_tools = os.path.abspath(os.path.join(BASE_DIR, "..", "..", "tools", "common_tools.py"))
assert os.path.exists(path_lenet), "{}不存在,请将lenet.py文件放到 {}".format(path_lenet, os.path.dirname(path_lenet))
assert os.path.exists(path_tools), "{}不存在,请将common_tools.py文件放到 {}".format(path_tools, os.path.dirname(path_tools))
import sys
hello_pytorch_DIR = os.path.abspath(os.path.dirname(__file__)+os.path.sep+".."+os.path.sep+"..")
sys.path.append(hello_pytorch_DIR)
from tools.my_dataset import RMBDataset
from tools.common_tools import set_seed, transform_invert
set_seed(1) # 设置随机种子
# 参数设置
MAX_EPOCH = 10
BATCH_SIZE = 1
LR = 0.01
log_interval = 10
val_interval = 1
rmb_label = {
"1": 0, "100": 1}
class AddPepperNoise(object):
"""增加椒盐噪声
Args:
snr (float): Signal Noise Rate
p (float): 概率值,依概率执行该操作
"""
def __init__(self, snr, p=0.9):
assert isinstance(snr, float) and (isinstance(p, float)) # 2020 07 26 or --> and
self.snr = snr
self.p = p
def __call__(self, img):
"""
Args:
img (PIL Image): PIL Image
Returns:
PIL Image: PIL image.
"""
if random.uniform(0, 1) < self.p:
img_ = np.array(img).copy()
h, w, c = img_.shape
# 信号的百分比
signal_pct = self.snr
# 噪声的百分比
noise_pct = (1 - self.snr)
# 通过0,1,2表示具体的选择
mask = np.random.choice((0, 1, 2), size=(h, w, 1), p=[signal_pct, noise_pct/2., noise_pct/2.])
mask = np.repeat(mask, c, axis=2)
img_[mask == 1] = 255 # 盐噪声 白色的
img_[mask == 2] = 0 # 椒噪声 黑色的
return Image.fromarray(img_.astype('uint8')).convert('RGB')
else:
return img
# ============================ step 1/5 数据 ============================
split_dir = os.path.abspath(os.path.join(BASE_DIR, "..", "..", "data", "rmb_split"))
if not os.path.exists(split_dir):
raise Exception(r"数据 {} 不存在, 回到lesson-06\1_split_dataset.py生成数据".format(split_dir))
train_dir = os.path.join(split_dir, "train")
valid_dir = os.path.join(split_dir, "valid")
norm_mean = [0.485, 0.456, 0.406]
norm_std = [0.229, 0.224, 0.225]
train_transform = transforms.Compose([
transforms.Resize((224, 224)),
AddPepperNoise(0.9, p=0.5),
transforms.ToTensor(),
transforms.Normalize(norm_mean, norm_std),
])
valid_transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(norm_mean, norm_std)
])
# 构建MyDataset实例
train_data = RMBDataset(data_dir=train_dir, transform=train_transform)
valid_data = RMBDataset(data_dir=valid_dir, transform=valid_transform)
# 构建DataLoder
train_loader = DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)
valid_loader = DataLoader(dataset=valid_data, batch_size=BATCH_SIZE)
# ============================ step 5/5 训练 ============================
for epoch in range(MAX_EPOCH):
for i, data in enumerate(train_loader):
inputs, labels = data # B C H W
img_tensor = inputs[0, ...] # C H W
img = transform_invert(img_tensor, train_transform)
plt.imshow(img)
plt.show()
plt.pause(0.5)
plt.close()
最後に、データ拡張の変換方法を要約します。
1. 切断
- 変換.CenterCrop
- 変換.RandomCrop
- 変換.RandomResizeCrop
- 変換.FiveCrop
- 変換.TenCrop
2.反転して回転します
- 変換.RandomhorizontalFlip
- 変換.RandomVerticalFlip
- 変換.RandomRotation
3. 画像変換
- 変換.パッド
- 変換.ColorJitter
- 変換.グレースケール
- 変換.RandomGrayscale
- 変換.RandomAffine
- 変換.LinearTransformation
- 変換.ランダム消去
- 変換.Lambda
- 変換.サイズ変更
- 変換.トーテンソル
- 変換する。正規化する
4 番目に、変換の操作
- 変換.ランダム選択
- 変換.RandomApply
- 変換.RandomOrder
データ強化の原則を強調します。つまり、トレーニング セットとテスト セットを近づけます。位置、グレースケール、変換塗りつぶしなどのいずれであっても、この方向で実行する必要があります。