code: def main(args):
      # Create model directory
      if not os.path.exists(args.model_path):
          os.makedirs(args.model_path)
  
      # Image preprocessing, normalization for the pretrained resnet
      transform = transforms.Compose(
          [
              transforms.RandomCrop(args.crop_size),
              transforms.RandomHorizontalFlip(),
              transforms.ToTensor(),
              transforms.Normalize(
                  (0.485, 0.456, 0.406), (0.229, 0.224, 0.225)
              ),
          ]
      )
  
      # Load vocabulary wrapper
      with open(args.vocab_path, "rb") as f:
          vocab = pickle.load(f)
  
      # Build data loader
      data_loader = get_loader(
          args.image_dir,
          args.caption_path,
          vocab,
          transform,
          args.batch_size,
          shuffle=True,
          num_workers=args.num_workers,
      )
  
      # Build the models
      encoder = EncoderCNN(args.embed_size).to(device)
      decoder = DecoderRNN(
          args.embed_size, args.hidden_size, len(vocab), args.num_layers
      ).to(device)
  
      # Loss and optimizer
      criterion = nn.CrossEntropyLoss()
      params = (
          list(decoder.parameters())
          + list(encoder.linear.parameters())
          + list(encoder.bn.parameters())
      )
      optimizer = torch.optim.Adam(params, lr=args.learning_rate)
  
      # Train the models
      total_step = len(data_loader)
      for epoch in range(args.num_epochs):
          for i, (images, captions, lengths) in enumerate(data_loader):
              # Set mini-batch dataset
              images = images.to(device)
              captions = captions.to(device)
              targets = pack_padded_sequence(
                  captions, lengths, batch_first=True
              )[0]
  
              # Forward, backward and optimize
              features = encoder(images)
              outputs = decoder(features, captions, lengths)
              loss = criterion(outputs, targets)
              decoder.zero_grad()
              encoder.zero_grad()
              loss.backward()
              optimizer.step()
  
              # Print log info
              if i % args.log_step == 0:
                  print(
                      "Epoch [{}/{}], Step [{}/{}], Loss: {:.4f}, Perplexity: {:5.4f}".format(
                          epoch,
                          args.num_epochs,
                          i,
                          total_step,
                          loss.item(),
                          np.exp(loss.item()),
                      )
                  )
  
              # Save the model checkpoints
              if (i + 1) % args.save_step == 0:
                  torch.save(
                      decoder.state_dict(),
                      os.path.join(
                          args.model_path,
                          "decoder-{}-{}.ckpt".format(epoch + 1, i + 1),
                      ),
                  )
                  torch.save(
                      encoder.state_dict(),
                      os.path.join(
                          args.model_path,
                          "encoder-{}-{}.ckpt".format(epoch + 1, i + 1),
                      ),
                  )
  
file_path: pytorch-tutorial/tutorials/03-advanced/image_captioning/train.py
imports: import argparse
  import torch
  import torch.nn
  import numpy
  import os
  import pickle
  from data_loader import get_loader
  from build_vocab import Vocabulary
  from model import EncoderCNN, DecoderRNN
  from torch.nn.utils.rnn import pack_padded_sequence
  from torchvision import transforms
label_desc: Use image node_id 1 for calling a global function with example usage: main(args) without return types