65605


Python3で実行しています。わかる方、よろしくお願いします。  
コード  
(unit.py)

[code]

    # coding: utf-8
    # $ wget https://people.eecs.berkeley.edu/~taesung_park/CycleGAN/datasets/apple2orange.zip
    # $ unzip apple2orange.zip
    # $ python unit.py --dataset_name apple2orange
    import argparse
    import os
    import numpy as np
    import math
    import itertools
    import datetime
    import time
    import sys
    
    import torchvision.transforms as transforms
    from torchvision.utils import save_image
    
    from torch.utils.data import DataLoader
    from torchvision import datasets
    from torch.autograd import Variable
    
    from models import *
    from datasets import *
    
    import torch.nn as nn
    import torch.nn.functional as F
    import torch
    
    parser = argparse.ArgumentParser()
    parser.add_argument('--epoch', type=int, default=0, help='epoch to start training from')
    parser.add_argument('--n_epochs', type=int, default=200, help='number of epochs of training')
    parser.add_argument('--dataset_name', type=str, default="apple2orange", help='name of the dataset')
    parser.add_argument('--batch_size', type=int, default=1, help='size of the batches')
    parser.add_argument('--lr', type=float, default=0.0001, help='adam: learning rate')
    parser.add_argument('--b1', type=float, default=0.5, help='adam: decay of first order momentum of gradient')
    parser.add_argument('--b2', type=float, default=0.999, help='adam: decay of first order momentum of gradient')
    parser.add_argument('--decay_epoch', type=int, default=100, help='epoch from which to start lr decay')
    parser.add_argument('--n_cpu', type=int, default=8, help='number of cpu threads to use during batch generation')
    parser.add_argument('--img_height', type=int, default=256, help='size of image height')
    parser.add_argument('--img_width', type=int, default=256, help='size of image width')
    parser.add_argument('--channels', type=int, default=3, help='number of image channels')
    parser.add_argument('--sample_interval', type=int, default=100, help='interval between sampling images from generators')
    parser.add_argument('--checkpoint_interval', type=int, default=-1, help='interval between saving model checkpoints')
    parser.add_argument('--n_downsample', type=int, default=2, help='number downsampling layers in encoder')
    parser.add_argument('--dim', type=int, default=64, help='number of filters in first encoder layer')
    opt = parser.parse_args()
    print(opt)
    
    cuda = True if torch.cuda.is_available() else False
    
    os.makedirs('images/%s' % opt.dataset_name, exist_ok=True)
    os.makedirs('saved_models/%s' % opt.dataset_name, exist_ok=True)
    
    # 損失
    criterion_GAN = torch.nn.MSELoss()
    criterion_pixel = torch.nn.L1Loss()
    
    # PatchGAN
    patch = (1, opt.img_height // 2**4, opt.img_width // 2**4)
    
    shared_dim = opt.dim * 2**opt.n_downsample
    
    # 生成機と判別機を作る
    shared_E = ResidualBlock(features=shared_dim)
    E1 = Encoder(dim=opt.dim, n_downsample=opt.n_downsample, shared_block=shared_E)
    E2 = Encoder(dim=opt.dim, n_downsample=opt.n_downsample, shared_block=shared_E)
    shared_G = ResidualBlock(features=shared_dim)
    G1 = Generator(dim=opt.dim, n_upsample=opt.n_downsample, shared_block=shared_G)
    G2 = Generator(dim=opt.dim, n_upsample=opt.n_downsample, shared_block=shared_G)
    D1 = Discriminator()
    D2 = Discriminator()
    
    if cuda:
        E1 = E1.cuda()
        E2 = E2.cuda()
        G1 = G1.cuda()
        G2 = G2.cuda()
        D1 = D1.cuda()
        D2 = D2.cuda()
        criterion_GAN.cuda()
        criterion_pixel.cuda()
    
    if opt.epoch != 0:
        E1.load_state_dict(torch.load('saved_models/%s/E1_%d.pth' % (opt.dataset_name, opt.epoch)))
        E2.load_state_dict(torch.load('saved_models/%s/E2_%d.pth' % (opt.dataset_name, opt.epoch)))
        G1.load_state_dict(torch.load('saved_models/%s/G1_%d.pth' % (opt.dataset_name, opt.epoch)))
        G2.load_state_dict(torch.load('saved_models/%s/G2_%d.pth' % (opt.dataset_name, opt.epoch)))
        D1.load_state_dict(torch.load('saved_models/%s/D1_%d.pth' % (opt.dataset_name, opt.epoch)))
        D2.load_state_dict(torch.load('saved_models/%s/D2_%d.pth' % (opt.dataset_name, opt.epoch)))
    else:
        E1.apply(weights_init_normal)
        E2.apply(weights_init_normal)
        G1.apply(weights_init_normal)
        G2.apply(weights_init_normal)
        D1.apply(weights_init_normal)
        D2.apply(weights_init_normal)
    
    lambda_0 = 10   # GAN
    lambda_1 = 0.1  # KL (生成された映像)
    lambda_2 = 100  # ID pixel-wise
    lambda_3 = 0.1  # KL (変換された映像)
    lambda_4 = 100  # Cycle pixel-wise
    
    # オプティマイザー
    optimizer_G = torch.optim.Adam(itertools.chain(E1.parameters(), E2.parameters(), G1.parameters(), G2.parameters()),
                                    lr=opt.lr, betas=(opt.b1, opt.b2))
    optimizer_D1 = torch.optim.Adam(D1.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))
    optimizer_D2 = torch.optim.Adam(D2.parameters(), lr=opt.lr, betas=(opt.b1, opt.b2))
    
    # 学習率の更新スケジュール
    lr_scheduler_G = torch.optim.lr_scheduler.LambdaLR(optimizer_G, lr_lambda=LambdaLR(opt.n_epochs, opt.epoch, opt.decay_epoch).step)
    lr_scheduler_D1 = torch.optim.lr_scheduler.LambdaLR(optimizer_D1, lr_lambda=LambdaLR(opt.n_epochs, opt.epoch, opt.decay_epoch).step)
    lr_scheduler_D2 = torch.optim.lr_scheduler.LambdaLR(optimizer_D2, lr_lambda=LambdaLR(opt.n_epochs, opt.epoch, opt.decay_epoch).step)
    
    Tensor = torch.cuda.FloatTensor if cuda else torch.Tensor
    
    # 画像変換
    transforms_ = [ transforms.Resize(int(opt.img_height*1.12), Image.BICUBIC), #リサイズ
                    transforms.RandomCrop((opt.img_height, opt.img_width)), #ランダムクロップ
                    transforms.RandomHorizontalFlip(), #ランダム水平反転
                    transforms.ToTensor(),
                    transforms.Normalize((0.5,0.5,0.5), (0.5,0.5,0.5)) ] #正則化
    
    # 学習データローダ
    dataloader = DataLoader(ImageDataset("%s" % opt.dataset_name, transforms_=transforms_, unaligned=True),
                            batch_size=opt.batch_size, shuffle=True, num_workers=opt.n_cpu)
    val_dataloader = DataLoader(ImageDataset("%s" % opt.dataset_name, transforms_=transforms_, unaligned=True, mode='test'),
                            batch_size=5, shuffle=True, num_workers=1)
    
    def sample_images(batches_done):
        imgs = next(iter(val_dataloader))
        X1 = Variable(imgs['A'].type(Tensor))
        X2 = Variable(imgs['B'].type(Tensor))
        _, Z1 = E1(X1)
        _, Z2 = E2(X2)
        fake_X1 = G1(Z2)
        fake_X2 = G2(Z1)
        img_sample = torch.cat((X1.data, fake_X2.data,
                                X2.data, fake_X1.data), 0)
        save_image(img_sample, 'images/%s/%s.png' % (opt.dataset_name, batches_done), nrow=5, normalize=True)
    
    def compute_kl(mu):
        mu_2 = torch.pow(mu, 2)
        loss = torch.mean(mu_2)
        return loss
    
    # 学習
    
    prev_time = time.time()
    for epoch in range(opt.epoch, opt.n_epochs):
        for i, batch in enumerate(dataloader):
    
            X1 = Variable(batch['A'].type(Tensor))
            X2 = Variable(batch['B'].type(Tensor))
    
            valid = Variable(Tensor(np.ones((X1.size(0), *patch))), requires_grad=False)
            fake = Variable(Tensor(np.zeros((X1.size(0), *patch))), requires_grad=False)
    
            # エンコーダとジェネレータを学習する
    
            optimizer_G.zero_grad()
    
            # 共有する潜在ベクトル
            mu1, Z1 = E1(X1)
            mu2, Z2 = E2(X2)
    
            # 再構成された画像
            recon_X1 = G1(Z1)
            recon_X2 = G2(Z2)
    
            # 変換された画像
            fake_X1 = G1(Z2)
            fake_X2 = G2(Z1)
    
            # サイクル変換
            mu1_, Z1_ = E1(fake_X1)
            mu2_, Z2_ = E2(fake_X2)
            cycle_X1 = G1(Z2_)
            cycle_X2 = G2(Z1_)
    
            # 損失
            loss_GAN_1 = lambda_0 * criterion_GAN(D1(fake_X1), valid)
            loss_GAN_2 = lambda_0 * criterion_GAN(D2(fake_X2), valid)
            loss_KL_1  = lambda_1 * compute_kl(mu1)
            loss_KL_1  = lambda_1 * compute_kl(mu2)
            loss_KL_1  = lambda_2 * criterion_pixel(recon_X1, X1)
            loss_ID_2  = lambda_2 * criterion_pixel(recon_X2, X2)
            loss_KL_1_ = lambda_3 * compute_kl(mu1_)
            loss_KL_2_ = lambda_3 * compute_kl(mu2_)
            loss_cyc_1 = lambda_4 * criterion_pixel(cycle_X1, X1)
            loss_cyc_2 = lambda_4 * criterion_pixel(cycle_X2, X2)
    
            # トータル損失
            loss_G =    loss_KL_1 + \
                        loss_KL_2 + \
                        loss_ID_1 + \
                        loss_ID_2 + \
                        loss_GAN_1 + \
                        loss_GAN_2 + \
                        loss_KL_1_ + \
                        loss_KL_2_ + \
                        loss_cyc_1 + \
                        loss_cyc_2
    
            loss_G.backward()
            optimizer_G.step()
    
            # 判別機1を訓練
    
            optimizer_D1.zero_grad()
    
            loss_D1 = criterion_GAN(D1(X1), valid) + \
                      criterion_GAN(D1(fake_X1.detach()), fake)
    
            loss_D1.backward()
            optimizer_D1.step()
    
            # 判別機2を訓練
    
            optimizer_D2.zero_grad()
    
            loss_D2 = criterion_GAN(D2(X2), valid) + \
                      criterion_GAN(D2(fake_X2.detach()), fake)
    
            loss_D2.backward()
            optimizer_D2.step()
    
            # ログ
            batches_done = epoch * len(dataloader) + i
            batches_left = opt.n_epochs * len(dataloader) - batches_done
            time_left = datetime.timedelta(seconds=batches_left * (time.time() - prev_time))
            prev_time = time.time()
    
            # Print log
            sys.stdout.write("\r[Epoch %d/%d] [Batch %d/%d] [D loss: %f] [G loss: %f] ETA: %s" %
                            (epoch, opt.n_epochs,
                            i, len(dataloader),
                            (loss_D1 + loss_D2).item(),
                            loss_G.item(),
                            time_left))
    
            if batches_done % opt.sample_interval == 0:
                sample_images(batches_done)
    
    
        # 学習率の更新
        lr_scheduler_G.step()
        lr_scheduler_D1.step()
        lr_scheduler_D2.step()
    
        if opt.checkpoint_interval != -1 and epoch % opt.checkpoint_interval == 0:
            torch.save(E1.state_dict(), 'saved_models/%s/ E1_%d.pth' % (opt.dataset_name, epoch))
            torch.save(E2.state_dict(), 'saved_models/%s/ E2_%d.pth' % (opt.dataset_name, epoch))
            torch.save(G1.state_dict(), 'saved_models/%s/ G1_%d.pth' % (opt.dataset_name, epoch))
            torch.save(G2.state_dict(), 'saved_models/%s/ G2_%d.pth' % (opt.dataset_name, epoch))
            torch.save(D1.state_dict(), 'saved_models/%s/ D1_%d.pth' % (opt.dataset_name, epoch))
            torch.save(D2.state_dict(), 'saved_models/%s/ D2_%d.pth' % (opt.dataset_name, epoch))
    
[/code]

(models.py)

[code]

    # coding:utf-8
    import torch.nn as nn
    import torch.nn.functional as F
    import torch
    from torch.autograd import Variable
    import numpy as np
    
    def weights_init_normal(m):
        classname = m.__class__.__name__
        if classname.find('Conv') != -1:
            torch.nn.init.normal_(m.weight.data, 0.0, 0.02)
        elif classname.find('BatchNorm2d') != -1:
            torch.nn.init.normal_(m.weight.data, 1.0, 0.02)
            torch.nn.init.constant_(m.bias.data, 0.0)
    
    class LambdaLR():
        def __init__(self, n_epochs, offset, decay_start_epoch):
            assert ((n_epochs - decay_start_epoch) > 0), "Decay must start before the training session ends!"
            self.n_epochs = n_epochs
            self.offset = offset
            self.decay_start_epoch = decay_start_epoch
    
        def step(self, epoch):
            return 1.0 - max(0, epoch + self.offset - self.decay_start_epoch)/(self.n_epochs - self.decay_start_epoch)
    
    
    # RESNET
    
    class ResidualBlock(nn.Module):
        def __init__(self, features):
            super(ResidualBlock, self).__init__()
    
            conv_block = [  nn.ReflectionPad2d(1),
                            nn.Conv2d(features, features, 3),
                            nn.InstanceNorm2d(features),
                            nn.ReLU(inplace=True),
                            nn.ReflectionPad2d(1),
                            nn.Conv2d(features, features, 3),
                            nn.InstanceNorm2d(features)  ]
    
            self.conv_block = nn.Sequential(*conv_block)
    
        def forward(self, x):
            return x + self.conv_block(x)
    
    #エンコーダ
    
    class Encoder(nn.Module):
        def __init__(self, in_channels=3, dim=64, n_downsample=2, shared_block=None):
            super(Encoder, self).__init__()
    
            # 最初の畳み込み層
            layers = [  nn.ReflectionPad2d(3),
                        nn.Conv2d(in_channels, dim, 7),
                        nn.InstanceNorm2d(64),
                        nn.LeakyReLU(0.2, inplace=True) ]
    
            # ダウンサンプリング
            for _ in range(n_downsample):
                layers += [ nn.Conv2d(dim, dim * 2, 4, stride=2, padding=1),
                            nn.InstanceNorm2d(dim * 2),
                            nn.ReLU(inplace=True) ]
                dim *= 2
    
            # 残渣ブロック
            for _ in range(3):
                layers += [ResidualBlock(dim)]
    
            self.model_blocks = nn.Sequential(*layers)
            self.shared_block = shared_block
    
        def reparameterization(self, mu):
            Tensor = torch.cuda.FloatTensor if mu.is_cuda else torch.FloatTensor
            z = Variable(Tensor(np.random.normal(0, 1, mu.shape)))
            return z + mu
    
        def forward(self, x):
            x = self.model_blocks(x)
            mu = self.shared_block(x)
            z = self.reparameterization(mu)
            return mu, z
    
    #生成機
    
    class Generator(nn.Module):
        def __init__(self, out_channels=3, dim=64, n_upsample=2, shared_block=None):
            super(Generator, self).__init__()
    
            self.shared_block = shared_block
    
            layers = []
            dim = dim * 2**n_upsample
            # 残渣ブロック
            for _ in range(3):
                layers += [ResidualBlock(dim)]
    
            # アップサンプリング
            for _ in range(n_upsample):
                layers += [ nn.ConvTranspose2d(dim, dim // 2, 4, stride=2, padding=1),
                            nn.InstanceNorm2d(dim // 2),
                            nn.LeakyReLU(0.2, inplace=True) ]
                dim = dim // 2
    
            # 出力
            layers += [ nn.ReflectionPad2d(3),
                        nn.Conv2d(dim, out_channels, 7),
                        nn.Tanh() ]
    
            self.model_blocks = nn.Sequential(*layers)
    
        def forward(self, x):
            x = self.shared_block(x)
            x = self.model_blocks(x)
            return x
    
    # 判別機
    
    class Discriminator(nn.Module):
        def __init__(self, in_channels=3):
            super(Discriminator, self).__init__()
    
            def discriminator_block(in_filters, out_filters, normalize=True):
                """Returns downsampling layers of each discriminator block"""
                layers = [nn.Conv2d(in_filters, out_filters, 4, stride=2, padding=1)]
                if normalize:
                    layers.append(nn.InstanceNorm2d(out_filters))
                layers.append(nn.LeakyReLU(0.2, inplace=True))
                return layers
    
            self.model = nn.Sequential(
                *discriminator_block(in_channels, 64, normalize=False),
                *discriminator_block(64, 128),
                *discriminator_block(128, 256),
                *discriminator_block(256, 512),
                nn.Conv2d(512, 1, 3, padding=1)
            )
    
        def forward(self, img):
            return self.model(img)
    
[/code]

(datasets.py)

[code]

    import glob
    import random
    import os
    
    from torch.utils.data import Dataset
    from PIL import Image
    import torchvision.transforms as transforms
    
    class ImageDataset(Dataset):
        def __init__(self, root, transforms_=None, unaligned=False, mode='train'):
            self.transform = transforms.Compose(transforms_)
            self.unaligned = unaligned
    
            self.files_A = sorted(glob.glob(os.path.join(root, '%s/A' % mode) + '/ *.*'))
            self.files_B = sorted(glob.glob(os.path.join(root, '%s/B' % mode) + '/ *.*'))
    
        def __getitem__(self, index):
            item_A = self.transform(Image.open(self.files_A[index % len(self.files_A)]))
    
            if self.unaligned:
                item_B = self.transform(Image.open(self.files_B[random.randint(0 , len(self.files_B) - 1)]))
            else:
                item_B = self.transform(Image.open(self.files_B[index % len(self.files_B)]))
    
            return {'A': item_A, 'B': item_B}
    
        def __len__(self):
            return max(len(self.files_A), len(self.files_B))
    
[/code]

エラーコード

[code]

    Traceback (most recent call last):
      File "unit.py", line 126, in <module>
        batch_size=opt.batch_size, shuffle=True, num_workers=opt.n_cpu)
      File "/Users/xxxxx/Library/Python/3.7/lib/python/site-packages/torch/utils/data/dataloader.py", line 213, in __init__
        sampler = RandomSampler(dataset)
      File "/Users/xxxxx/Library/Python/3.7/lib/python/site-packages/torch/utils/data/sampler.py", line 94, in __init__
        "value, but got num_samples={}".format(self.num_samples))
    ValueError: num_samples should be a positive integer value, but got num_samples=0
    
[/code]

(追記)  
フォルダ構造  
Unitフォルダの中に  
1.apple2orange(データセット)  
2.datasets.py  
3.models.py  
4.unit.py  
が入っています。


CC BY-SA 4.0
2020-04-13T08:04:57.400
65585
2020-04-14T11:57:43.287
2020-04-14T04:01:32.227
question
python
ValueError: num_samples should be a positive integer value, but got num_samples=0というエラーが出てしまう。


取り敢えず質問の件での問題は判明しました。

`datasets.py`の以下の行が間違いですね。

[code]

    self.files_A = sorted(glob.glob(os.path.join(root, '%s/A' % mode) + '/ *.*'))
    self.files_B = sorted(glob.glob(os.path.join(root, '%s/B' % mode) + '/ *.*'))
    
[/code]

実際のフォルダ構成は以下のようになっています。

[code]

    apple2orange
        +-- testA
        +-- testB
        +-- trainA
        +-- trainB
    
[/code]

プログラムでは`'%s/A'`,`'%s/B'`と、`mode`変数名(trainまたはtest)のフォルダの下に、また`A`と`B`のフォルダがあることになっていますが、実際には`mode`変数名と`A`,`B`が連結された名前のフォルダになっています。

それから続くワイルドカード指定の文字列に空白が混じってこうなって`'/
*.*'`いることも問題です。これだとファイル名の最初が空白で始まっている必要があります。

上記2つが原因で、データファイルが無いという状況になり、質問のエラーが発生することとなっています。

`datasets.py`の該当行を以下のように修正すれば、この問題は解決できるでしょう。

[code]

    self.files_A = sorted(glob.glob(os.path.join(root, '%sA' % mode) + '/*.*'))
    self.files_B = sorted(glob.glob(os.path.join(root, '%sB' % mode) + '/*.*'))
    
[/code]

ただし、まだ別の問題がありそうですね。  
動作環境あるいはパラメータ指定の問題かもしれませんが、私の環境(Windows10 1909, 64bit Python 3.8.2, numpy
1.18.2, torch 1.4.0+cpu, torchvision 0.5.0+cpu, tensorflow
2.2.0rc2)でパラメータは無指定で実行すると問題個所は通りましたが、その後、以下のようなエラーが発生しました。

[code]

    C:\Develop\Python\apple2orange>py unit.py
    Namespace(b1=0.5, b2=0.999, batch_size=1, channels=3, checkpoint_interval=-1, dataset_name='apple2orange', decay_epoch=100, dim=64, epoch=0, img_height=256, img_width=256, lr=0.0001, n_cpu=8, n_downsample=2, n_epochs=200, sample_interval=100)
    Namespace(b1=0.5, b2=0.999, batch_size=1, channels=3, checkpoint_interval=-1, dataset_name='apple2orange', decay_epoch=100, dim=64, epoch=0, img_height=256, img_width=256, lr=0.0001, n_cpu=8, n_downsample=2, n_epochs=200, sample_interval=100)
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "C:\Users\UserName\AppData\Local\Programs\Python\Python38\lib\multiprocessing\spawn.py", line 116, in spawn_main
        exitcode = _main(fd, parent_sentinel)
      File "C:\Users\UserName\AppData\Local\Programs\Python\Python38\lib\multiprocessing\spawn.py", line 125, in _main
        prepare(preparation_data)
      File "C:\Users\UserName\AppData\Local\Programs\Python\Python38\lib\multiprocessing\spawn.py", line 236, in prepare
        _fixup_main_from_path(data['init_main_from_path'])
      File "C:\Users\UserName\AppData\Local\Programs\Python\Python38\lib\multiprocessing\spawn.py", line 287, in _fixup_main_from_path
        main_content = runpy.run_path(main_path,
      File "C:\Users\UserName\AppData\Local\Programs\Python\Python38\lib\runpy.py", line 263, in run_path
        return _run_module_code(code, init_globals, run_name,
      File "C:\Users\UserName\AppData\Local\Programs\Python\Python38\lib\runpy.py", line 96, in _run_module_code
        _run_code(code, mod_globals, init_globals,
      File "C:\Users\UserName\AppData\Local\Programs\Python\Python38\lib\runpy.py", line 86, in _run_code
        exec(code, run_globals)
      File "C:\Develop\Python\apple2orange\unit.py", line 150, in <module>
        for i, batch in enumerate(dataloader):
      File "C:\Users\UserName\AppData\Local\Programs\Python\Python38\lib\site-packages\torch\utils\data\dataloader.py", line 279, in __iter__
        return _MultiProcessingDataLoaderIter(self)
      File "C:\Users\UserName\AppData\Local\Programs\Python\Python38\lib\site-packages\torch\utils\data\dataloader.py", line 719, in __init__
        w.start()
      File "C:\Users\UserName\AppData\Local\Programs\Python\Python38\lib\multiprocessing\process.py", line 121, in start
        self._popen = self._Popen(self)
      File "C:\Users\UserName\AppData\Local\Programs\Python\Python38\lib\multiprocessing\context.py", line 224, in _Popen
        return _default_context.get_context().Process._Popen(process_obj)
      File "C:\Users\UserName\AppData\Local\Programs\Python\Python38\lib\multiprocessing\context.py", line 326, in _Popen
        return Popen(process_obj)
      File "C:\Users\UserName\AppData\Local\Programs\Python\Python38\lib\multiprocessing\popen_spawn_win32.py", line 45, in __init__
        prep_data = spawn.get_preparation_data(process_obj._name)
      File "C:\Users\UserName\AppData\Local\Programs\Python\Python38\lib\multiprocessing\spawn.py", line 154, in get_preparation_data
        _check_not_importing_main()
      File "C:\Users\UserName\AppData\Local\Programs\Python\Python38\lib\multiprocessing\spawn.py", line 134, in _check_not_importing_main
        raise RuntimeError('''
    RuntimeError:
            An attempt has been made to start a new process before the
            current process has finished its bootstrapping phase.
    
            This probably means that you are not using fork to start your
            child processes and you have forgotten to use the proper idiom
            in the main module:
    
                if __name__ == '__main__':
                    freeze_support()
                    ...
    
            The "freeze_support()" line can be omitted if the program
            is not going to be frozen to produce an executable.
    
[/code]

* * *

上記を`python
freeze_support()`で検索したら、マルチプロセスで実行する時の定型らしいですね。以下のような記事がいくつも出てくるので、やりたいことに応じて対処する必要があるでしょう。  
まあオプションでマルチプロセスをしないように出来れば、それでも動くとは思いますが。

[Python で並列処理(初めての人向け)](http://iatlex.com/python/parallel_first)  
[並列処理でのランタイムエラー（ if __name__ == '__main__':
freeze_support()...）](https://teratail.com/questions/211011)  
[Pythonのmultiprocessing.poolが実行されない](https://ja.stackoverflow.com/q/55885/26370)  
[Pythonでmultiprocessingを用いて並列実行するときに、プロセスそれぞれの進捗バーをtqdmで表示する](https://qiita.com/nkato_/items/9e2c75013d44050851ed)  
[freeze_support - multiprocessing - Python documentation -
Kite](https://kite.com/python/docs/multiprocessing.freeze_support)  
[multiprocessing ---
プロセスベースの並列処理](https://docs.python.org/ja/3/library/multiprocessing.html)


CC BY-SA 4.0
2020-04-14T06:42:10.750
65605
2020-04-14T11:57:43.287
2020-04-14T11:57:43.287
26370
26370
65585
answer