def generate_inf_cases(train, seed, board_size=25, min_dens=0.01, max_dens=0.99, warm_up=5, min_delta=1, max_delta=5, dtype=np.int, return_one_but_last=False): rs = np.random.RandomState(seed) zer = np.zeros(shape=(board_size, board_size), dtype=dtype) while True: density = rs.uniform(min_dens, max_dens) start = rs.choice([1, 0], size=(board_size, board_size), p=[density, 1.0 - density]) for _ in range(warm_up): start = life_step(start) delta = rs.randint(min_delta, max_delta + 1) stop = start.copy() one_but_last = None for _ in range(delta): one_but_last = stop stop = life_step(stop) if not (stop == zer).all(): if return_one_but_last: yield delta, one_but_last, stop elif train: yield delta, start, stop else: yield delta, stop
def test_sim_toad(): toad_1 = np.array([[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 0], [0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]) toad_2 = np.array([[0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0], [0, 1, 0, 0, 1, 0], [0, 1, 0, 0, 1, 0], [0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0]]) assert (life_step(toad_1) == toad_2).all() assert (life_step(toad_2) == toad_1).all()
def test_generate_train_set(): xs = list(generate_train_set(10, 234)) assert len(xs) == 10 for delta, start, stop in xs: for i in range(delta): start = life_step(start) assert (start == stop).all()
def test_sim_block(): block = np.array([ [0, 0, 0, 0], [0, 1, 1, 0], [0, 1, 1, 0], [0, 0, 0, 0], ]) assert (life_step(block) == block).all()
def test_step_back_all_3x3(alg_class): alg = alg_class(tile_graph) for Aorig in tqdm(generate_all(3, 3)): X = np.copy(Aorig) X = life_step(X) A = alg.step_back(X) sc = score(1, A, X) assert sc == 1.0
def train(self, delta, start, stop): X = start m = X.shape[0] n = X.shape[1] for s in range(delta): Y = life_step(X) if s < delta - 1 else stop for i in range(m): for j in range(n): a_id = self.__get_tile_id(X, i, j) b_id = self.__get_tile_id(Y, i, j) self.trans[a_id][b_id] += 1
def preprocess(): # Tiles - all possible titles 3x3 T = list(generate_all(3, 3)) assert (len(T) == 512) # Backward possibilities # dict: central bit -> list of possible prev tiles 3x3 B = [[], []] for i, tile in enumerate(T): next_tile = life_step(tile) B[int(next_tile[1][1])].append(i) # Fun fact: # >>> len(B[0]) # 372 # >>> len(B[1]) # 140 # Matrix horiz[i,j] - true if tile j can be put horizontally on the right side of tile i. # 0 0 1 0 1 0 0 0 1 0 # 0[1]0 + 1[0]0 = 0[1|0]0 # 0 0 0 0 0 1 0 0 0 1 horiz = np.zeros((len(T), len(T)), dtype=np.bool) # Matrix verti[i,j] - true if tile j can be put vertically under tile i. verti = np.zeros((len(T), len(T)), dtype=np.bool) # Diagonal relationships -- they don't seem to be needed. #diago_se = np.zeros((len(T), len(T)), dtype=np.bool) #diago_sw = np.zeros((len(T), len(T)), dtype=np.bool) for i, x in enumerate(T): for j, y in enumerate(T): # Check if left tile is compatible with the right tile. # I.e., left tile's right side is equal to right tile's left side. horiz[i, j] = (x[:, (1, 2)] == y[:, (0, 1)]).all() # Check if upper tile is compatible with the lower tile. # I.e., upper tile's lower side is equal to lower tile's upper side. verti[i, j] = (x[(1, 2), :] == y[(0, 1), :]).all() # Diagonal relationships -- they don't seem to be needed. #diago_se[i, j] = (x[(1, 2), (1, 2)] == y[(0, 1), (0, 1)]).all() #diago_sw[i, j] = (x[(1, 2), (0, 1)] == y[(0, 1), (1, 2)]).all() return T, B, horiz, verti
def test_step_back_simple(alg_class): alg = alg_class(tile_graph) block = np.array([ [0, 0, 0, 0], [0, 1, 1, 0], [0, 1, 1, 0], [0, 0, 0, 0], ]) A = alg.step_back(block) B = life_step(A) assert (B == block).all() toad_1 = np.array([[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 0], [0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]) toad_2 = np.array([[0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0], [0, 1, 0, 0, 1, 0], [0, 1, 0, 0, 1, 0], [0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0]]) A = alg.step_back(toad_2) assert (A == toad_1).all()
def score(delta, start, stop): X = start for i in range(delta): X = life_step(X) return np.count_nonzero(X == stop) / stop.size
def likely_starts(delta, stop): # Just test a couple of "likely" starting boards and pick the best one. starts = [np.zeros_like(stop), stop, life_step(stop)] scores = [score(delta, start, stop) for start in starts] idx = np.argmax(scores) return starts[idx]
def sim_density(delta, stop): # If stop is a still board, then return that. Otherwise, return zeros. n = life_step(stop) return stop if (stop == n).all() else np.zeros_like(stop)
def train( gen_arch, fwd_arch, device, writer, batchSize, niter, lr, beta1, dry_run, outf, workers=1, start_iter=0, gen_path=None, fwd_path=None, ngpu=1, nz=100, epoch_samples=64*100, learn_forward=True, sigmoid=True): os.makedirs(outf, exist_ok=True) gen_path = gen_path if start_iter == 0 else os.path.join(outf, f'netG_epoch_{start_iter-1}.pth') fwd_path = fwd_path if start_iter == 0 else os.path.join(outf, f'netF_epoch_{start_iter-1}.pth') # Prediction threshold pred_th = 0.5 if sigmoid else 0.0 dataset = DataGenerator(823131 + start_iter, sigmoid) dataloader = torch.utils.data.DataLoader(dataset, batch_size=batchSize, shuffle=False, num_workers=int(workers)) val_size = 1024 val_set = bitmap.generate_test_set(set_size=val_size, seed=9568382) deltas_val, stops_val = cnnify_batch(zip(*val_set)) ones_val = np.ones_like(deltas_val) noise_val = torch.randn(val_size, nz, 1, 1, device=device) netG = get_generator_net(gen_arch).to(device) init_model(netG, gen_path) netF = get_forward_net(fwd_arch).to(device) init_model(netF, fwd_path) criterion = nn.BCELoss() fixed_noise = torch.randn(batchSize, nz, 1, 1, device=device) fixed_ones = np.ones((batchSize,), dtype=np.int) # setup optimizer optimizerD = optim.Adam(netF.parameters(), lr=lr, betas=(beta1, 0.999)) optimizerG = optim.Adam(netG.parameters(), lr=lr, betas=(beta1, 0.999)) scores = [] for i in range(5): noise = torch.randn(val_size, nz, 1, 1, device=device) one_step_pred_batch = (netG(torch.Tensor(stops_val).to(device), noise) > pred_th).cpu() model_scores = scoring.score_batch(ones_val, np.array(one_step_pred_batch, dtype=np.bool), stops_val) scores.append(model_scores) zeros = np.zeros_like(one_step_pred_batch, dtype=np.bool) zeros_scores = scoring.score_batch(ones_val, zeros, stops_val) scores.append(zeros_scores) best_scores = np.max(scores, axis=0) print( f'Mean error one step: model {1 - np.mean(model_scores)}, zeros {1 - np.mean(zeros_scores)}, ensemble {1 - np.mean(best_scores)}') #for epoch in range(opt.niter): epoch = start_iter samples_in_epoch = 0 samples_before = start_iter * epoch_samples for j, data in enumerate(dataloader, 0): i = start_iter * epoch_samples // batchSize + j ############################ # (1) Update F (forward) network -- in the original GAN, it's a "D" network (discriminator) # Original comment: Update D network: maximize log(D(x)) + log(1 - D(G(z))) ########################### # train with real starting board -- data set provides ground truth netF.zero_grad() start_real_cpu = data[0].to(device) stop_real_cpu = data[1].to(device) batch_size = start_real_cpu.size(0) output = netF(start_real_cpu) errD_real = criterion(output, stop_real_cpu) if learn_forward: errD_real.backward() D_x = (output.round().eq(stop_real_cpu)).sum().item() / output.numel() # train with fake -- use simulator (life_step) to generate ground truth # TODO: replace with fixed forward model (should be faster, in batches and on GPU) noise = torch.randn(batch_size, nz, 1, 1, device=device) fake = netG(stop_real_cpu, noise) fake_np = (fake > pred_th).detach().cpu().numpy() fake_next_np = life_step(fake_np) fake_next = torch.tensor(fake_next_np, dtype=torch.float32).to(device) output = netF(fake.detach()) errD_fake = criterion(output, fake_next) if learn_forward: errD_fake.backward() D_G_z1 = (output.round().eq(fake_next)).sum().item() / output.numel() errD = errD_real + errD_fake if learn_forward: optimizerD.step() # just for reporting... true_stop_np = (stop_real_cpu > pred_th).detach().cpu().numpy() fake_scores = scoring.score_batch(fixed_ones, fake_np, true_stop_np, show_progress=False) fake_mae = 1 - fake_scores.mean() fake_density = fake_np.mean() ############################ # (2) Update G network: maximize log(D(G(z))) ########################### netG.zero_grad() output = netF(fake) errG = criterion(output, stop_real_cpu) errG.backward() D_G_z2 = (output.round().eq(fake_next)).sum().item() / output.numel() optimizerG.step() samples_in_epoch += batch_size s = samples_before + samples_in_epoch writer.add_scalar('Loss/forward', errD.item(), i) writer.add_scalar('Loss/gen', errG.item(), i) writer.add_scalar('MAE/train', fake_mae.item(), i) print('[%d/%d][%d] Loss_F: %.4f Loss_G: %.4f fwd acc(real): %.2f fwd acc(fake): %.2f / %.2f, fake dens: %.2f, MAE: %.4f' % (epoch, start_iter+niter, i, errD.item(), errG.item(), D_x, D_G_z1, D_G_z2, fake_density, fake_mae)) if samples_in_epoch >= epoch_samples: """ multi_step_pred_batch = predict(netG, deltas_val, stops_val, fixed_noise) multi_step_mean_err = 1 - np.mean(scoring.score_batch(deltas_val, np.array(multi_step_pred_batch, dtype=np.bool), stops_val)) """ one_step_pred_batch = (netG(torch.Tensor(stops_val).to(device), noise_val) > pred_th).detach().cpu().numpy() one_step_mean_err = 1 - np.mean(scoring.score_batch(ones_val, np.array(one_step_pred_batch, dtype=np.bool), stops_val)) print(f'Mean error: one step {one_step_mean_err}') writer.add_scalar('MAE/val', one_step_mean_err, epoch) vutils.save_image(start_real_cpu, '%s/real_samples.png' % outf, normalize=True) fake = netG(stop_real_cpu, fixed_noise).detach() vutils.save_image(fake, '%s/fake_samples_epoch_%03d.png' % (outf, epoch), normalize=True) grid = vutils.make_grid(start_real_cpu) writer.add_image('real', grid, epoch) grid = vutils.make_grid(fake) writer.add_image('fake', grid, epoch) # do checkpointing torch.save(netG.state_dict(), '%s/netG_epoch_%d.pth' % (outf, epoch)) torch.save(netF.state_dict(), '%s/netF_epoch_%d.pth' % (outf, epoch)) epoch += 1 samples_in_epoch = 0 if epoch - start_iter >= niter: break if dry_run: break return one_step_mean_err