def assert_gradient_descent(objective, model): params, image = param.image(224, batch=2) optimizer = torch.optim.Adam(params, lr=0.05) T = render.hook_model(model, image) objective_f = objectives.as_objective(objective) model(image()) start_value = objective_f(T) for _ in range(NUM_STEPS): optimizer.zero_grad() model(image()) loss = objective_f(T) loss.backward() optimizer.step() end_value = objective_f(T) assert start_value > end_value
def test_cppn_fits_xor(): params, image = param.cppn(16) optimizer = torch.optim.Adam(params, lr=0.01) objective_f = objectives.as_objective(xor_loss) for _ in range(200): optimizer.zero_grad() loss = objective_f(image()) loss.backward() optimizer.step() vis = image()[0] close_enough = (vis[:, 0, 0].mean() > .99 and vis[:, -1, -1].mean() > .99 and vis[:, -1, 0].mean() < .01 and vis[:, 0, -1].mean() < .01) if close_enough: return assert False, "fitting XOR took more than 200 steps, failing test"
def render_vis( model, objective_f, param_f=None, optimizer=None, transforms=None, thresholds=(512, ), verbose=False, preprocess=True, progress=True, show_image=True, save_image=False, image_name=None, show_inline=False, fixed_image_size=None, ): if param_f is None: param_f = lambda: param.image(128) # param_f is a function that should return two things # params - parameters to update, which we pass to the optimizer # image_f - a function that returns an image as a tensor params, image_f = param_f() if optimizer is None: optimizer = lambda params: torch.optim.Adam(params, lr=5e-2) optimizer = optimizer(params) if transforms is None: transforms = transform.standard_transforms transforms = transforms.copy() if preprocess: if model._get_name() == "InceptionV1": # Original Tensorflow InceptionV1 takes input range [-117, 138] transforms.append(transform.preprocess_inceptionv1()) else: # Assume we use normalization for torchvision.models # See https://pytorch.org/docs/stable/torchvision/models.html transforms.append(transform.normalize()) # Upsample images smaller than 224 image_shape = image_f().shape if fixed_image_size is not None: new_size = fixed_image_size elif image_shape[2] < 224 or image_shape[3] < 224: new_size = 224 else: new_size = None if new_size: transforms.append( torch.nn.Upsample(size=new_size, mode="bilinear", align_corners=True)) transform_f = transform.compose(transforms) hook = hook_model(model, image_f) objective_f = objectives.as_objective(objective_f) if verbose: model(transform_f(image_f())) print("Initial loss: {:.3f}".format(objective_f(hook))) images = [] try: for i in tqdm(range(1, max(thresholds) + 1), disable=(not progress)): optimizer.zero_grad() try: model.encode_image(transform_f(image_f())) except RuntimeError as ex: if i == 1: # Only display the warning message # on the first iteration, no need to do that # every iteration warnings.warn( "Some layers could not be computed because the size of the " "image is not big enough. It is fine, as long as the non" "computed layers are not used in the objective function" f"(exception details: '{ex}')") loss = objective_f(hook) loss.backward() optimizer.step() if i in thresholds: image = tensor_to_img_array(image_f()) if verbose: print("Loss at step {}: {:.3f}".format( i, objective_f(hook))) if show_inline: show(image) images.append(image) except KeyboardInterrupt: print("Interrupted optimization at step {:d}.".format(i)) if verbose: print("Loss at step {}: {:.3f}".format(i, objective_f(hook))) images.append(tensor_to_img_array(image_f())) if save_image: export(image_f(), image_name) if show_inline: show(tensor_to_img_array(image_f())) elif show_image: view(image_f()) return images
def render_vis(model, objective_f, param_f=None, optimizer=None, transforms=None, thresholds=(512, ), verbose=False, preprocess=True, progress=True, show_image=True, save_image=False, image_name=None, show_inline=False): if param_f is None: param_f = lambda: param.image(128) # param_f is a function that should return two things # params - parameters to update, which we pass to the optimizer # image_f - a function that returns an image as a tensor params, image_f = param_f() if optimizer is None: optimizer = lambda params: torch.optim.Adam(params, lr=5e-2) optimizer = optimizer(params) if transforms is None: transforms = transform.standard_transforms.copy() if preprocess: if model._get_name() == "InceptionV1": # Original Tensorflow InceptionV1 takes input range [-117, 138] transforms.append(transform.preprocess_inceptionv1()) else: # Assume we use normalization for torchvision.models # See https://pytorch.org/docs/stable/torchvision/models.html transforms.append(transform.normalize()) # Upsample images smaller than 224 image_shape = image_f().shape if image_shape[2] < 224 or image_shape[3] < 224: transforms.append( torch.nn.Upsample(size=224, mode='bilinear', align_corners=True)) transform_f = transform.compose(transforms) hook = hook_model(model, image_f) objective_f = objectives.as_objective(objective_f) if verbose: model(transform_f(image_f())) print("Initial loss: {:.3f}".format(objective_f(hook))) images = [] try: for i in tqdm(range(1, max(thresholds) + 1), disable=(not progress)): optimizer.zero_grad() model(transform_f(image_f())) loss = objective_f(hook) loss.backward() optimizer.step() if i in thresholds: image = tensor_to_img_array(image_f()) if verbose: print("Loss at step {}: {:.3f}".format( i, objective_f(hook))) if show_inline: show(image) images.append(image) except KeyboardInterrupt: print("Interrupted optimization at step {:d}.".format(i)) if verbose: print("Loss at step {}: {:.3f}".format(i, objective_f(hook))) images.append(tensor_to_img_array(image_f())) if save_image: export(image_f(), image_name) if show_inline: show(tensor_to_img_array(image_f())) elif show_image: view(image_f()) return images