Open wan2355 opened 3 years ago
異常画像の検出のところで、画像を作成する際に、以下のアラートが出るので少し修正しました。
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
追加部:
# joined_imageの値が-1~1 なので、値の範囲をを0~1に変更する。
joined_image = np.clip(joined_image, 0.0, 1.0)
# 以下はもとのまま
plt.figure(figsize=(14, 6))
plt.imshow(joined_image)
...
ありがとうございます。
chapter8_grayscaleというブランチを切って、私の方でもgrayscaleで学習してみるコードを追加しました。
確かに、そのまま用いると再構成した画像は問題ないもののzから生成した画像はぼやけているようでした。
再構成だけうまくいくという事はおそらく、L1_loss(loss_ge_2)が効きすぎている事が原因だと考えられるので、以下のコード部分のalphaをもっと小さい値にしてみてください。
loss_ge_1 = criterion(p_fake, y_true) + criterion(p_true, y_fake)
loss_ge_2 = criterion_L1(x, G_E_x) + criterion_L1(z, E_G_z)
alpha = 0.01
loss_ge = (1 - alpha)*loss_ge_1 + alpha*loss_ge_2
loss_ge.backward(retain_graph=True)
optimizer_ge.step()
試しにalpha=0.001に変更して学習してみたところ、epoch500時点ですが↓のようにzから生成した画像の質は高くなっているようでした。
ただしalphaを小さくしすぎるとL1_lossを導入していないEfficientGANと同様に再構成性能が低くなってしまうので、注意してください。
ほおずきの画像をgrayscaleにする際は以下のように、RGBをgrayscaleにした後に3channelsに変換しています。
もし元々grayscaleの画像の場合はtransforms.Grayscale(num_output_channels=1), # RGB to grayscale
の箇所を外してください。
# 画像を読み込む際の前処理
transform_dict = {
"train": transforms.Compose(
[
transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)), # IMAGE_SIZEにreshape
transforms.RandomHorizontalFlip(), # ランダムに左右反転を行う
transforms.ToTensor(),
transforms.Grayscale(num_output_channels=1), # RGB to grayscale
transforms.Lambda(lambda x: x.repeat(3, 1, 1)), # grayscale to 3channels
]
),
"test": transforms.Compose(
[
transforms.Resize((IMAGE_SIZE, IMAGE_SIZE)), # IMAGE_SIZEにreshape
transforms.ToTensor(),
transforms.Grayscale(num_output_channels=1), # RGB to grayscale
transforms.Lambda(lambda x: x.repeat(3, 1, 1)), # grayscale to 3channels
]
),
}
massy様
ご丁寧な回答ありがとうございます。 また結果までご提示いただき、大変参考になります。 会社のPCで計算しているため、ご提案の以下2点は月曜日に確認させていただきます。
> alphaを小さくする。例 0.01 > 0.01 など。
(お手を煩わせて恐縮ですが、)いくつか質問させてください。
Q1: 再構成画像ですが、3000epochあたりで飽和しているようで、これ以降は画質が向上しません。 画質を向上させる方法があれば、お教えください。
generatorのmodelを他のものにするのは1つの方法だと思います。 一方、このmodelのまま、parameterやcodeの修正で対応可能でしょうか。
Q2: L1正則を使う意味は、dataのsparse性を利用するということでしょうか。 その場合、L1正則のかわりに、L2正則など少し緩い条件を利用するのは如何でしょうか。 ただpytorchにL2正則があるのかどうかは調べてないとわかりませんが。
数学的なところは正確に理解してませんが、他のところで似たような経験があり、気になったので質問した次第です。 もしご存知でしたらお教えください。
Q3: 手元のPCでdataサイズを256x256pxにすると、時々 の以下のようなアラート(error?)が出ます(手元にPCがないので、正確には少し違うかもしれません)。 結果は大丈夫そうなので気にしてませんが、もしアラートを無くす方法があればお教えください。 ただcolabでは試してません。
File "/home/hoge/anaconda3/lib/python3.7/multiprocessing/connection.py", line 368, in _send n = write(self._handle, buf) BrokenPipeError: [Errno 32] Broken pipe
対策として、以下などを見出しましたが、正確に理解できてません。 (hardwareよりな問題のようで、私は知識が不足しております。)
対応として以下が提案されてますが、本programに利用できるのでしょうか。
From my point of view there could be 2 solutions for these, 1st ) Increasing Swap 2nd ) Re-run the optimisation program every time after n number of simulations(200 in this case).
上記について、もしご存知でしたらお教えください。
Q1: 再構成画像ですが、3000epochあたりで飽和しているようで、これ以降は画質が向上しません。 画質を向上させる方法があれば、お教えください。 generatorのmodelを他のものにするのは1つの方法だと思います。 一方、このmodelのまま、parameterやcodeの修正で対応可能でしょうか。
A1: anomaly_scoreが徐々に下がっていっているけれど一定の値からは下がらなくなっているのであればモデルとしての表現力の限界なような気がします。
そのため、一番はやはりモデルをよりリッチな構造にするのが解決策となりそうです。 Residual Blockなどを導入してみると良いかもしれません。 また、基本的にはgeneratorを変えるだけでなく、discriminatorと(おそらく)encoderも一緒に変更した方が良いです。
Q2: L1正則を使う意味は、dataのsparse性を利用するということでしょうか。 その場合、L1正則のかわりに、L2正則など少し緩い条件を利用するのは如何でしょうか。 ただpytorchにL2正則があるのかどうかは調べてないとわかりませんが。 数学的なところは正確に理解してませんが、他のところで似たような経験があり、気になったので質問した次第です。 もしご存知でしたらお教えください。
A2: L1_lossを利用しているのは基本的にこの論文を参考にしました。L2_lossでも問題無いと思います。
試しにalpha=0.001でL2_lossを用いてみるnotebookもコミットしました。
ただ、結果はanomaly_scoreでL1_lossと比較すると、少し劣っていて生成画像を見るとヘタ部分などがややぼやけているようでした。 Lossの値のスケールが異なるのでalphaの値をいろいろ変更してみるとL1_lossより良い結果になる可能性はあると思います。
Q3: 手元のPCでdataサイズを256x256pxにすると、時々 の以下のようなアラート(error?)が出ます(手元にPCがないので、正確には少し違うかもしれません)。 結果は大丈夫そうなので気にしてませんが、もしアラートを無くす方法があればお教えください。 ただcolabでは試してません。
A3: このエラーログだけだと原因はわかりませんでした。もしよろしければどのコード箇所でこのエラーが出るのかと、詳細なエラーログを共有いただけますと助かります。
massy様
ご丁寧な回答ありがとうございます。 またいろいろ試していただき感謝します。
1.
一番はやはりモデルをよりリッチな構造にするのが解決策となりそうです。 ... また、基本的にはgeneratorを変えるだけでなく、discriminatorと(おそらく)encoderも一緒に変更した方が良いです。
generatorとdiscriminatorのほうはあてがあるのですが、encoderはどうしたら良いのかわかりません。 もし何か提案があれば、おねがいします。
2.
Residual Blockなどを導入してみると良いかもしれません。
どの辺りに挿入すれば良さそうでしょうか。
3.
ただ、結果はanomaly_scoreでL1_lossと比較すると、少し劣っていて生成画像を見るとヘタ部分などがややぼやけているようでした。 Lossの値のスケールが異なるのでalphaの値をいろいろ変更してみるとL1_lossより良い結果になる可能性はあると思います。
後ほど試してみます。
4.
このエラーログだけだと原因はわかりませんでした。もしよろしければどのコード箇所でこのエラーが出るのかと、詳細なエラーログを共有いただけますと助かります。
了解です。 会社にありますので、明日以降にご連絡します。
1.
generatorとdiscriminatorのほうはあてがあるのですが、encoderはどうしたら良いのかわかりません。 もし何か提案があれば、おねがいします。
私も正直なにが正解なのかわかりませんが、おそらくgeneratorと対照的になるように組んでいくのが良いと思います。
2.
Residual Blockなどを導入してみると良いかもしれません。
どの辺りに挿入すれば良さそうでしょうか。
EfficientGANはネットワークの中身に対する制約や条件はないので、インプットとアウトプットさえ同様であれば他のGANのネットワーク構成を利用する事ができると考えています。そのため、SRGANの構成を真似してResidual Blockで組んでみたり、あるいはStyleGANなどを元にしたりするのが良い気がします。
おはようございます。
なぜかAlertが出ませんね。。。
connection.py...
の後に、BrokenPipeError が出ていたと思います。
codeは少し修正しましたが、概ね書籍に記載のものです。 trainingの部分はshellの上で動くように少し書き足しており、元のcodeより倍以上に長いです。 必要でしたらのせますが、このコメントに添付すれば良いでしょうか。
set parameter
EPOCHS = 2000
alpha = 0.001
--------------------------------------------------
make dir & save parameter
--------------------------------------------------
load data
--------------------------------------------------
job start at : 210405-1113
--------------------------------------------------------------------------------
50/2000 epoch ge_loss: 23.876 d_loss: 0.000 anomaly_score: 14679.339
Traceback (most recent call last):
100/2000 epoch ge_loss: 12.711 d_loss: 0.002 anomaly_score: 8709.387
Traceback (most recent call last):
150/2000 epoch ge_loss: 7.085 d_loss: 0.000 anomaly_score: 7005.530
200/2000 epoch ge_loss: 5.269 d_loss: 0.001 anomaly_score: 6286.282
Traceback (most recent call last):
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/queues.py", line 242, in _feed
send_bytes(obj)
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/connection.py", line 200, in send_bytes
self._send_bytes(m[offset:offset + size])
250/2000 epoch ge_loss: 6.519 d_loss: 0.002 anomaly_score: 13678.796
Traceback (most recent call last):
300/2000 epoch ge_loss: 4.368 d_loss: 0.000 anomaly_score: 10557.472
Traceback (most recent call last):
350/2000 epoch ge_loss: 5.719 d_loss: 0.053 anomaly_score: 9013.415
Traceback (most recent call last):
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/queues.py", line 242, in _feed
send_bytes(obj)
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/connection.py", line 200, in send_bytes
self._send_bytes(m[offset:offset + size])
400/2000 epoch ge_loss: 4.226 d_loss: 0.000 anomaly_score: 12985.966
450/2000 epoch ge_loss: 3.975 d_loss: 0.002 anomaly_score: 9752.129
Traceback (most recent call last):
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/queues.py", line 242, in _feed
send_bytes(obj)
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/connection.py", line 200, in send_bytes
self._send_bytes(m[offset:offset + size])
--------------------------------------------------------------------------------
500/2000 epoch ge_loss: 4.030 d_loss: 0.002 anomaly_score: 10607.229
550/2000 epoch ge_loss: 4.692 d_loss: 0.002 anomaly_score: 11019.177
600/2000 epoch ge_loss: 3.528 d_loss: 0.001 anomaly_score: 12155.901
Traceback (most recent call last):
Traceback (most recent call last):
650/2000 epoch ge_loss: 3.483 d_loss: 0.003 anomaly_score: 11777.586
Traceback (most recent call last):
Traceback (most recent call last):
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/queues.py", line 242, in _feed
send_bytes(obj)
700/2000 epoch ge_loss: 3.026 d_loss: 0.005 anomaly_score: 10709.992
750/2000 epoch ge_loss: 2.851 d_loss: 0.002 anomaly_score: 11048.208
800/2000 epoch ge_loss: 3.836 d_loss: 0.016 anomaly_score: 11319.374
850/2000 epoch ge_loss: 3.183 d_loss: 0.003 anomaly_score: 10832.643
900/2000 epoch ge_loss: 2.866 d_loss: 0.012 anomaly_score: 12896.531
Traceback (most recent call last):
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/queues.py", line 242, in _feed
send_bytes(obj)
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/connection.py", line 200, in send_bytes
self._send_bytes(m[offset:offset + size])
950/2000 epoch ge_loss: 3.068 d_loss: 0.003 anomaly_score: 11054.521
Traceback (most recent call last):
Traceback (most recent call last):
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/queues.py", line 242, in _feed
send_bytes(obj)
Traceback (most recent call last):
--------------------------------------------------------------------------------
1000/2000 epoch ge_loss: 2.743 d_loss: 0.002 anomaly_score: 10287.133
1050/2000 epoch ge_loss: 2.587 d_loss: 0.004 anomaly_score: 10776.228
Traceback (most recent call last):
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/queues.py", line 242, in _feed
send_bytes(obj)
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/connection.py", line 200, in send_bytes
self._send_bytes(m[offset:offset + size])
1100/2000 epoch ge_loss: 2.704 d_loss: 0.005 anomaly_score: 11400.406
Traceback (most recent call last):
1150/2000 epoch ge_loss: 2.775 d_loss: 0.009 anomaly_score: 10556.899
Traceback (most recent call last):
1200/2000 epoch ge_loss: 2.804 d_loss: 0.006 anomaly_score: 9656.309
1250/2000 epoch ge_loss: 2.428 d_loss: 0.002 anomaly_score: 10194.276
Traceback (most recent call last):
Traceback (most recent call last):
1300/2000 epoch ge_loss: 2.338 d_loss: 0.012 anomaly_score: 8073.060
Traceback (most recent call last):
Traceback (most recent call last):
1350/2000 epoch ge_loss: 2.808 d_loss: 0.004 anomaly_score: 7809.970
Traceback (most recent call last):
1400/2000 epoch ge_loss: 2.285 d_loss: 0.011 anomaly_score: 8665.330
Traceback (most recent call last):
1450/2000 epoch ge_loss: 2.325 d_loss: 0.062 anomaly_score: 7551.758
Traceback (most recent call last):
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/queues.py", line 242, in _feed
send_bytes(obj)
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/connection.py", line 200, in send_bytes
self._send_bytes(m[offset:offset + size])
Traceback (most recent call last):
--------------------------------------------------------------------------------
1500/2000 epoch ge_loss: 2.163 d_loss: 0.106 anomaly_score: 6416.862
Traceback (most recent call last):
1550/2000 epoch ge_loss: 2.181 d_loss: 0.024 anomaly_score: 7069.499
1600/2000 epoch ge_loss: 2.366 d_loss: 0.011 anomaly_score: 7249.777
1650/2000 epoch ge_loss: 2.240 d_loss: 0.007 anomaly_score: 8008.530
Traceback (most recent call last):
Traceback (most recent call last):
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/queues.py", line 242, in _feed
send_bytes(obj)
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/connection.py", line 200, in send_bytes
self._send_bytes(m[offset:offset + size])
Traceback (most recent call last):
1700/2000 epoch ge_loss: 2.225 d_loss: 0.012 anomaly_score: 7994.545
Traceback (most recent call last):
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/queues.py", line 242, in _feed
send_bytes(obj)
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/connection.py", line 200, in send_bytes
self._send_bytes(m[offset:offset + size])
1750/2000 epoch ge_loss: 2.233 d_loss: 0.013 anomaly_score: 8151.363
1800/2000 epoch ge_loss: 2.241 d_loss: 0.015 anomaly_score: 6139.832
Traceback (most recent call last):
Traceback (most recent call last):
Traceback (most recent call last):
1850/2000 epoch ge_loss: 2.477 d_loss: 0.007 anomaly_score: 8778.923
Traceback (most recent call last):
Traceback (most recent call last):
1900/2000 epoch ge_loss: 2.066 d_loss: 0.048 anomaly_score: 6449.593
1950/2000 epoch ge_loss: 2.393 d_loss: 0.016 anomaly_score: 7315.434
Traceback (most recent call last):
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/queues.py", line 242, in _feed
send_bytes(obj)
File "/home/wan/.pyenv/versions/3.7.7/lib/python3.7/multiprocessing/connection.py", line 200, in send_bytes
self._send_bytes(m[offset:offset + size])
--------------------------------------------------------------------------------
job time 16.0 min
先程tarnsformのところをご提案のように修正しましたところ、動かなくなりました。 よく見ると、dataloaderのところで、以下のようにRGBに変換されておりますので、
def load_image(self, path):
image = Image.open(path).convert("RGB")
tensor_image = self.transform(image)
return tensor_image
先日ご提案の以下は不要と思います。
transforms.Grayscale(num_output_channels=1), # RGB to grayscale
transforms.Lambda(lambda x: x.repeat(3, 1, 1)), # grayscale to 3channels
ただ、PILでRGBに変換するのと、gray-scaleのlayerを3つ重ねるのはできる画像が異なるような気がしますので、
Image.open(path).convert("RGB") を Image.open(path)
にして上記を試してみます。
Image.open(path).convert("RGB") を Image.open(path)
で試してみました。 当初の convert("RGB") より良くないですが、 Alpha=0.01よりはよさそうです。
anomaly_scoreはAlpha=0.001が最も小さいです。 一方、Alphaを下げたため、生成される再構成画像が少し荒くなるようです(中央がAlpha=0.001)。
以上から、Alphaを下げると、image_from_zの画質が向上し、anomaly_scoreが小さくなるようですが、再構成画像の質は劣化するのですね。
ただし、Image.open(path) でgra-scale画像を読み込んで、 transforms.Lambda(lambda x: x.repeat(3, 1, 1)) でRGBにした場合に、Image.open(path).convert("RGB") より、悪いのが不明です。 私がcodeを間違ったかもしれませんので、後ほど確認します。
↓再構成 上:Image.open(path).convert("RGB") , Alpha=0.01 中:Image.open(path).convert("RGB") , Alpha=0.001 下: Image.open(path), Alpha=0.001
↓image_from_z 上:Image.open(path).convert("RGB") , Alpha=0.01 中:Image.open(path).convert("RGB") , Alpha=0.001 下: Image.open(path), Alpha=0.001
↓異常部検知 上:Image.open(path).convert("RGB") , Alpha=0.01 中:Image.open(path).convert("RGB") , Alpha=0.001 下: Image.open(path), Alpha=0.001
↓tensorboard 赤色: Alpha+0.01 橙色:Image.open(path).convert("RGB") 灰色: Image.open(path)
なぜかやたらとzから生成した画像の質が悪いのが気になりますね・・ 256×256用にwan2355様で変更されたネットワークの組み方があまり良くないのかも知れません。
96×96ですが、一度下のnotebookを元に試してもらってもよろしいでしょうか?
早速の回答ありがとうございます。 後ほど、試してみます。
私の組み直したnetworkですが、originalから大きく変えてないですが、kernelとか一部修正してます。 encoder, generator, discriminatorは以下です。
class Encoder(nn.Module):
def __init__(self):
super().__init__()
self.main = nn.Sequential(
nn.Conv2d(3, 32, kernel_size=6, stride=4, padding=1, bias=False),
nn.BatchNorm2d(32),
nn.LeakyReLU(0.2), #256x256 > 128
nn.Conv2d(32, 64, kernel_size=4, stride=2, padding=1, bias=False),
nn.BatchNorm2d(64),
nn.LeakyReLU(0.2), #128 > 64
nn.Conv2d(64, 128, kernel_size=4, stride=2, padding=1, bias=False),
nn.BatchNorm2d(128),
nn.LeakyReLU(0.2), #64x64 > 32
nn.Conv2d(128, 256, kernel_size=6, stride=4, padding=1, bias=False),
nn.BatchNorm2d(256),
nn.LeakyReLU(0.2), #32x32 > 8 ここだけ 1/4
## modify kernel 6 > 8
nn.Conv2d(256, 512, kernel_size=8, stride=2, bias=False),
nn.BatchNorm2d(512),
nn.LeakyReLU(0.2), #8x8 > 1
nn.Conv2d(512, 512, kernel_size=1, stride=1, bias=False),
nn.BatchNorm2d(512),
nn.LeakyReLU(0.2), #1x1
)
self.last = nn.Sequential(
nn.Conv2d(512, EMBED_SIZE, kernel_size=1, stride=1, bias=False)
)
class Generator(nn.Module):
def __init__(self):
super().__init__()
self.main = nn.Sequential(
## kernel 6x6 >> 8x8
nn.ConvTranspose2d(EMBED_SIZE, 256, kernel_size=8, stride=1, padding=0, bias=False), # 1x1 > 8x8
nn.BatchNorm2d(256),
nn.LeakyReLU(0.2),
## ここだけ4倍
nn.ConvTranspose2d(256, 128, kernel_size=4, stride=4, bias=False), # 8 > 32
nn.BatchNorm2d(128),
nn.LeakyReLU(0.2),
nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1, bias=False), # 32 > 64
nn.BatchNorm2d(64),
nn.LeakyReLU(0.2),
nn.ConvTranspose2d(64, 32, kernel_size=4, stride=2, padding=1, bias=False), # 64 > 128
nn.BatchNorm2d(32),
nn.LeakyReLU(0.2),
nn.ConvTranspose2d(32, 3, kernel_size=4, stride=2, padding=1, bias=False), # 128 > 256
nn.Tanh()
)
def forward(self, z):
out = self.main(z)
return out
class Discriminator(nn.Module):
def __init__(self):
super().__init__()
self.x_layer = nn.Sequential(
nn.Conv2d(ch, 32, kernel_size=3, stride=1, padding=1),
# nn.BatchNorm2d(32),
nn.LeakyReLU(0.1, inplace=True), #256 > 128
nn.Dropout2d(p=0.3),
nn.AvgPool2d(2),
nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1),
nn.BatchNorm2d(64),
nn.LeakyReLU(0.1, inplace=True), # 128 > 64
nn.Dropout2d(p=0.3),
nn.AvgPool2d(2),
nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
nn.BatchNorm2d(128),
nn.LeakyReLU(0.1, inplace=True), # 64 >32
nn.Dropout2d(p=0.3),
nn.AvgPool2d(2),
nn.Conv2d(128, 256, kernel_size=6, stride=4, padding=1),
nn.BatchNorm2d(256),
nn.LeakyReLU(0.1, inplace=True), #32 > 8 ここだけ 1/4
nn.Dropout2d(p=0.3),
## kernel 4 > 1
nn.AvgPool2d(2),
nn.Conv2d(256, 256, kernel_size=4, stride=1) # 8 > 1x1
)
今試してますが、頂いたmodelではimage_from_zは私のよりよいです。 modelを修正してみます。
image_form_z 上:私 下:massy103 様
image_from_zが悪い理由がわかりました。 discriminatorが良くなかったようです。
以下の部分で画像サイズを1/8にしたのが原因のようです。
nn.AvgPool2d(2), # input-size * 1/2
nn.Conv2d(128, 256, kernel_size=6, stride=4, padding=1), # input-size * 1/4
以下のようにkernelの大きさetcを変更して計算中。
nn.AvgPool2d(2), # 64 > 32
nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1),
nn.AvgPool2d(2), # 16=32/2
nn.Conv2d(256, 256, kernel_size=16, stride=1) # 16 > 1x1
結果:image_from_z 良くなりました。ただもう少し良くしたい。Alphaを振ってみますか? それと上記のAlertもなくなりました。
上:修正前 下:修正後
改善したようでよかったです。
基本的に、discriminatorではconv2dでは画像サイズは変わらないようにして、nn.AvgPool2dで減らしていく方が(経験則的に)質が良いです。
さらに改善するとすれば、ネットワークをもっと深くするか、他のGANのアーキテクチャを真似するしかないと思います。
例えば以下でkernel_size=16で一気に1×1にしてしまっている箇所をより細かいkernel_sizeとnn.AvgPool2dの組み合わせで深くしていくようなところでしょうか。
nn.Conv2d(256, 256, kernel_size=16, stride=1) # 16 > 1x1
massy様
夜分失礼します。。。 回答ありがとうございます。
以下はかなり参考になりました。
discriminatorではconv2dでは画像サイズは変わらないようにして、nn.AvgPool2dで減らしていく方が(経験則的に)質が良いです。
帰り際に自分のdataを先程修正したmodelで計算させていたのですが、当初と大きく変わりませんでした。少し良くなった程度です。 alphaを0.0001まで減らしてみましたが、あまり改善しません。 再構成画像のほうが劣化してくだけのようでした。 がっかりしたので、月曜日ですが、ビールを買って帰宅しました。
明日以下で試してみようと考えていますが、他にご提案があればお願いします。 また無意味なものがありましたら(多くがそうかも知れませんが)、ご指摘ください。 無駄なことをせずにすみますので。 おやすみなさい。
zの次元を上げてみる。(embeded...?) 私の画像はオセロのような白黒の円盤を盤面に並べたような感じです。ただ円盤が白黒ではなく、細かい階調のあり、形がもっと複雑なコマが多数乗っていると想像してください。 かなり直感的な推測ですが、demo画像と私のdataでは表現されるべき次元の数が違いすぎるのかなと思い、この項目を考えた次第です。
gray-scaleなので、期待は少ないですが、 画像を標準化?してみる(平均=0, 標準偏差=1など)。 もしくは階調の幅を最大化する。
画像を読み込む際にgrayのまま読み込み、3層重ねてRGBにして計算してみる。
modelの入力channelを1にする。
上記で駄目なら、他のmodelを試す。(かなり時間がかかりそう。)
お疲れ様です。 大変苦労されているようですね。。
1のzの次元は改善できる可能性あると思いますが、(個人的な経験では)学習がうまくいかない時にzの次元をいじっても特に良くなった事はないです。とりあえず一度、256とかで試してみるのが良いかと思います。
2~4はあまり重要ではないと思います。
なので、大変だとは思いますがやはり5が一番改善に寄与すると思います。
あまり情報量が無いので参考にはならないかも知れませんが、styleGANを使ったanoGANでうまくいったという記事もありました。
時間があるときに私の方でも、grayscaleのほおずきの画像で256×256でzからの生成画像が綺麗になるように調整してみます。
massyさま
回答ありがとうございます。 ご協力に感謝します。 modelを直すしかなそうですか。。。
難しい提案ですが、 私が検討しているdataの一部をお渡しできるなら、それでご検討は可能でしょうか。
内容確認したので、削除しておきました!
2については少し検討したいので、お時間いただけますと幸いです。
massy様
削除していただき、ありがとうございます。 2についてもご検討よろしくおねがいします。 可能になりそうでしたら、私のemail-addressをご連絡します。
@wan2355
2について、前向きに検討したいと思っているので一度詳しく内容をお聞きしてもよろしいでしょうか。
後ほどこちらから宛先にご連絡します。
massy さま
先日はお騒がせしたわり、良い結果にならず、申し訳ありませんでした。 私のdataをお見せできない状況は変わらないのですが、現在のprogramを修正して、画像の質をと異常検出の精度を向上せねばなりません。 generator, discriminator, eoncoderなどprogramをどのように改善してゆけばよいのか再度ご意見をいただけないでしょうか。 また可能でありましら、鬼灯などfruits-360の画像で結構ですので、さらに画質と異常検知の質を高めるようご検討頂けるとありがたいです。
StyleGANのprogramを本書で読んでみましたが、内容が高度で理解が難しいので、これを組み込むにはかなり時間がかりそうです。
お手数をおかけしますが、ご検討よろしくおねがいします。
massy様
ご無沙汰しております。 少し改良してみました。 小川雄太郎先生の本に掲載されているSelf_Attentionを導入してみました。内容は正確には理解できていません。 GeneratorとDiscriminator さらにEncorderも導入してみましたが、Encoderの方はあまり効果はなさそうです。
結果: 以下のとおりです。 (閲覧後は図を削除して頂いてもよろしいでしょうか。tensorboradの結果は残しても良いです。)
図は 上: originalのまま. 中央: GeneratorとDiscriminator にself-attention 下: GeneratorとDiscriminator さらにEncorderにself attention
画像はお米のCT像(約500~600枚)を使いました。 (お米を使ったのは特に意味はありません。評価例です。) 画像サイズは256x256. epoch=2000 alpha = 0.001 (0.005でも大きな変化はないが、0.01では画像が劣化する。) self-attentionはメモリの消費が大きく、batch-size 3が最大です。 (私がoriginalよりもlayerを増やしていることも、影響してます。) colabでも試しましたが3が最大でした。
課題: 1.512x512以上の画像で行いたかが、どうすればメモリを消費を抑えられるか。 2.精度を上げたいので、zの画像をもう少し向上させたい?(これ以外に指標があればお教えください。) 3.今回は立てたお米のみを正常として利用したが、寝かせたものや上下逆さにしたものも撮影(もしくはaugmentation?)して、正常の画像の種類を増やしたほうが良いのか、それともこのままの画像で他のお米と比較したほうが良いのか。(撮影時にお米を一定の方向に向けるのは難しいため。) ただ寝かせた画像などを入れると、精度が低下しないか懸念される。 3.AnoGAN以外のmodelや評価方法の検討も必要か。 4.もし撮影して比較したい試料があればご提案頂けるとありがたいです。 サイズは数mm以下。 うち装置はX線のエネルギーが低いため、昆虫などでも撮影は可能です。逆に分厚い金属を含む試料は無理です。
また可能であれば、programをみて頂けるとありがたいのですが、如何でしょうか。
Tensorboard
@wan2355 様
GeneratorとDiscriminator さらにEncorderも導入してみましたが、Encoderの方はあまり効果はなさそうです。
EncoderはAnoGANで必要な推論時の勾配法によるz探索をなくすためにあるので、AnoGANに導入してもあまり効果はないと思います。
1.512x512以上の画像で行いたかが、どうすればメモリを消費を抑えられるか。
基本的にはバッチサイズを下げるか、ネットワークを浅くするしかなさそうです。 バッチサイズはどれだけ下げても4は欲しいので、今回はネットワークを浅くするしかないかもです
2.精度を上げたいので、zの画像をもう少し向上させたい?(これ以外に指標があればお教えください。)
指標としてはFIDやInception Scoreがあり、最終的に生成された画像に対する定量的な評価には使えると思います。
3.今回は立てたお米のみを正常として利用したが、寝かせたものや上下逆さにしたものも撮影(もしくはaugmentation?)して、正常の画像の種類を増やしたほうが良いのか、それともこのままの画像で他のお米と比較したほうが良いのか。(撮影時にお米を一定の方向に向けるのは難しいため。) ただ寝かせた画像などを入れると、精度が低下しないか懸念される。
これについてはなんとも言えないですね・・ どちらかと言うとこのままの画像で他のお米と比較したほうが良いと思います。
3.AnoGAN以外のmodelや評価方法の検討も必要か。
個人的にはAnoGANは推論に時間がかかり、従来のEfficientGANは再構成画像の質が悪い(ずれる)ので、L1_Lossを導入したEfficientGANが使い勝手がよく、画像の質も良いと思っています。 L1_Lossを導入したEfficientGANで、ネットワークを今のシンプルなものからリッチなものにするのが一番筋が良い気がします。
4.もし撮影して比較したい試料があればご提案頂けるとありがたいです。 サイズは数mm以下。 うち装置はX線のエネルギーが低いため、昆虫などでも撮影は可能です。逆に分厚い金属を含む試料は無理です。 また可能であれば、programをみて頂けるとありがたいのですが、如何でしょうか。
逆に、もし可能であればお米の画像を共有していただけると時間のあるときに私の方で実験してみたいのですが可能でしょうか?
massy 様
早速のご連絡ありがとうございます。
もし可能であればお米の画像を共有していただけると時間のあるときに私の方で実験してみたいのですが可能でしょうか?
このdataは共有可能ですが、他の人には渡さないでください。見せる程度は大丈夫です。 jpgなので、100MBもありません。 どうやってお送りしましょうか。
補足ですが、米の両脇のものは米を保持するためのholderで、 本来は不要ですが、画像処理でうまく除去できていません。 これをうまく取り除けると精度が上がりそうです。
バッチサイズはどれだけ下げても4は欲しいので、今回はネットワークを浅くするしかないかもです 了解です。 こちらでも検討してみます。
個人的にはAnoGANは推論に時間がかかり、従来のEfficientGANは再構成画像の質が悪い(ずれる)ので、L1_Lossを導入したEfficientGANが使い勝手がよく、画像の質も良いと思っています。 L1_Lossを導入したEfficientGANで、ネットワークを今のシンプルなものからリッチなものにするのが一番筋が良い気がします。
了解です。 私の質問はEfficientGAN_L1以外のmodelの意味でした。 現状では今のmodelが良さそうですが、目標としてはもっと大きな画像で、画像の質も上げたい です。
よろしくおねがいします。
@wan2355
このdataは共有可能ですが、他の人には渡さないでください。見せる程度は大丈夫です。 jpgなので、100MBもありません。 どうやってお送りしましょうか。
先日ご連絡したgmailのアカウントに添付していただくか、google driveにアップロードして私のgmailアカウントに権限付与していただく形だと良いかと思います!
先程共有しました。 ご確認願います。
万一 dataが別でしたら、ご連絡ください。
20:42 先程 評価用のtest画像も共有しました。
ありがとうございます。 確認しました。
dataの共有は外しても大丈夫でしょうか。
@wan2355
遅くなりました。 ダウンロードしたので外して大丈夫です!
massy103様 ご無沙汰しております。 少し進展しましたので、ご相談です。 以前と同様に閲覧後に画像と各modelのfileは削除頂けませんか。 お願いします。
今回の修正点。 小川先生のSelf-Attentionを利用させていただき、元のmodelを修正してみました。 Alphaも変化させてみたところ、以下の条件で画質が向上してきました。 ただ以前より計算に時間がかかります。
PS: いまcolabでも計算させてますが、異常に遅いです。 利用者が多いためですかね。 奇妙なことにcolabで計算させるとまともな画像が得られませんね?
ご相談内容:
1.良くなってきましたが、さらに画質を上げたい。またもっと大きな高精細な画像を扱いたい。 2.画像の明るさやコントラスがanoscoreに与える影響が大きいので、画像の明るさやコントラストをどのように決めれば、anoscoreが安定するのか。 3.予測画像が元の画像を向きと一致しないことがある。
条件: α=0.008 epoch =3101 batch size=5 残りはほぼdefaultのまま。
z_image 以前より良くなったと思います。
異常検知画像 2-1. 割れたもの 割れが大きいほど、anoscoreも大きくなっており、興味深い。 予測画像が異なるのが気になる。
2-2. 傾いた試料への対応。 下の段の画像は試料の傾斜に予測画像がうまく対応しているが、 上の段では予測画像が元の画像の方向と一致しない。 学習画像にこちらの方向のdataが少ないためかもしれない。
2-3.明るさ・コントラストを変えると、Anoscoreが変わる。 どうやって画像の明るさやコントラストを決めるか難しい。
参考までにdiscriminator, generatorの構造です。 いろいろ変えたいのですが、これ以上 各parameterの値を大きくするとメモリに乗りません。。。 また私の力量不足でこれ以上layerをへらすと画質が劣化します。
discriminator
generator
確認したので画像を削除しました! いったん現時点での所感をコメントさせていただきます 週末になにか試せたらまた追記します
1.良くなってきましたが、さらに画質を上げたい。またもっと大きな高精細な画像を扱いたい。
zからの生成画像がかなりよくなったと感じました。 もう少し高品質でメモリ効率の良いモデルをリサーチしてみますね。
2.画像の明るさやコントラスがanoscoreに与える影響が大きいので、画像の明るさやコントラストをどのように決めれば、anoscoreが安定するのか。
これはanomaly scoreの測り方を変えるのが良いと思います。 おそらく、輪郭のみを見れば良いと思うのでanomaly scoreを測定する関数内部で二値化をしてから誤差を測るのが良いかもです。(あまり詳しくないので二値化より良い処理がありそうです) 二値化の閾値は米の主要な輪郭が残るように決めてください。
3.予測画像が元の画像を向きと一致しないことがある。
(もしL1損失を損失関数に含めているのであれば、)これは予測画像の欠損・異常部分が無かったとした場合の(正常な)形状の画像が学習画像になく、そのような画像に対応する潜在変数が無いからだと思います。 異常検知的な観点でいくと、感覚的な話しですが部分的なパーツが欠損しているという異常よりも、見たことない向きという異常の方が異常度が高いので、ある意味で向きも再現できないのは自然な印象です。
massy 様
貴重なコメントありがとうございます。
もう少し高品質でメモリ効率の良いモデルをリサーチしてみますね。
期待しております。 私の方でも使えそうな物があれば試してみます。
これはanomaly scoreの測り方を変えるのが良いと思います。
codeをよく見て考えてみます。 今回も学習にかける前に手動で画像の背景が消えるようにはしてますが、限界があります。
また測定によって画像の明るさがばらつくため、学習にかける前に画像全体を規格化できる方法を考えています。 これはこれ以外の方法でも苦心しているところです。 検討はしてますが、良い方法が見つかっていません。
引き続きよろしくお願いします。
二値化してみましたが、目的にはあいませんでした。 (opencvで二値化しようとしたため、float32をunit8への変換は厄介でした。)
微細な形態が消えるのと、内部の構造(クラックなど)も画像によっては消失してしまうため、現状のほうが良さそうです。 また考えてみます。
massy 様
少し余談ですが、以下のように画像をtensorboardxに表示できるようにしては如何でしょうか。 tensorboardx以下の数行がそれです。
元のcodeとは少し変わっているかもしれませんが、おそらく大丈夫と思います。 lossと画像が一度に見れるので、便利と思います。
from torchvision.utils import make_grid, save_image
## save images every 50 epochs
if epoch % 50 == 0:
model_G.eval()
model_D.eval()
model_E.eval()
save_image_size_for_z = min(BATCH_SIZE, 8)
save_images = model_G(z)
save_image(save_images[:save_image_size_for_z], f"{SAVE_IMAGE_FROM_Z_PATH}/epoch_{epoch:05}.png", nro
w=4)
## tensorboardx
z_img = make_grid(save_images[:save_image_size_for_z])
writer.add_image( "0_z_img", z_img, epoch)
save_image_size_for_recon = min(BATCH_SIZE, 8)
images = x[:save_image_size_for_recon]
G_E_x = model_G(model_E(images))
diff_images = torch.abs(images - G_E_x)
comparison = torch.cat([images , G_E_x, diff_images]).to("cpu")
save_image(comparison, f"{SAVE_IMAGE_RECONSTRUCT}/epoch_{epoch:05}.png", nrow=save_image_size_for_recon)
## tensorboardx
org_img = make_grid(images)
writer.add_image( "1_org_img", org_img, epoch)
E_img = make_grid(G_E_x)
writer.add_image( "2_E_img", E_img, epoch)
diff_img = make_grid(diff_images)
writer.add_image( "3_diff_img", diff_img, epoch)
本書のEfficientGANを試しています。 以下の症状ですが、知見がございましたら、よろしくお願いします。
1. gray-scale画像、約250枚。サイズは256x256pxです。 このためmodelを少し修正してます。 layerを追加したり、kernel-sizeを変更したりしましたが、結果は同じでした。 10000 epochほど学習させました。
2. reconstructした画像はお見せできませんが、ほぼ元の画像を再現できています。 しかしやや不鮮明です(medianをかけたような画像。)。 またimage_from_zの画像もコントラストが低く、不鮮明な画像が生成されます。
Encoderがうまく学習されていないのでしょうか? また生成される画像のレンジ(0~256に収まっていない?)がおかしいのでしょうか。
3. 上記のためか、異常画像から生成される異常検知画像が不鮮明で、元画像の正常な部分もあまり正確に再現できていません。 このため、差分の画像も異常部は多少表現されていますが、不十分です。 Anomaly_scoreは20000~40000です。
昨年、別のEfficientGANを試したことがありましたが、異常検知画像は元画像の正常な部分をほとんど再現できませんでした。 Anomaly_scoreは4000~6000でした。
4. 参考までに、テンソルボードの結果は以下です(まだ学習途中です)。 ・赤がmodelのlayerを1つ増やしたもの。 ・橙色はlayerは増やさずに、kernelの大きさを修正したもの。
・・・・・・・・・・・・・・・・・・・・・・・・・・・・ 4/2追記
確認の為、上記のkernel修正modelを使い、「鬼灯(ホオズキ)」約250枚をグレースケールにし、さらに100pxから256pxにして検証しみました。 2000epochの学習をさせました。
reconstrutの方(中段)は元の「鬼灯」(上段)がやや微けてますが、再現できています。 image_from_zもボケてはいますが、鬼灯らしい形が見えてきてます。 異常部の検出も悪くない。
すると、私のdataが問題なのでしょうか。
以下の画像を参照ください。
↓ reconstrust
↓ image_from_z
↓ 異常部の検出
↓ Tensorboard 青が鬼灯(RGB) 空色が鬼灯(Gray) 赤色が私のdata