文章目录
作业介绍
- 作业主页:Assignment #3
- 作业目的:
- 作业源代码:
RNN_Captioning.ipynb
0. 准备
在这个练习中,您将实现一个普通的递归神经网络,并使用它们来训练一个可以为图像生成描述的模型。
安装H5py:
我们将使用的COCO数据集以HDF5格式存储。要加载HDF5文件,我们需要安装h5py
Python包。
pip install h5py
初始设置
# As usual, a bit of setup
import time, os, json
import numpy as np
import matplotlib.pyplot as plt
from cs231n.gradient_check import eval_numerical_gradient, eval_numerical_gradient_array
from cs231n.rnn_layers import *
from cs231n.captioning_solver import CaptioningSolver
from cs231n.classifiers.rnn import CaptioningRNN
from cs231n.coco_utils import load_coco_data, sample_coco_minibatch, decode_captions
from cs231n.image_utils import image_from_url
%matplotlib inline
plt.rcParams['figure.figsize'] = (10.0, 8.0) # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'
# for auto-reloading external modules
# see http://stackoverflow.com/questions/1907993/autoreload-of-modules-in-ipython
%load_ext autoreload
%autoreload 2
def rel_error(x, y):
""" returns relative error """
return np.max(np.abs(x - y) / (np.maximum(1e-8, np.abs(x) + np.abs(y))))
1. Microsoft COCO
对于这个练习,我们将使用2014年发布的Microsoft COCO数据集,它已经成为图像字幕的标准测试数据集。该数据集包括80,000张训练图像和40,000张验证图像,每个图像都有5个由Amazon Mechanical Turk上的工作人员编写的注释。
我们已经对数据进行了预处理并为您提取了特征。
对于所有的图像,我们提取了在ImageNet上预先训练的VGG-16网络的fc7层的特征.这些特性存储在train2014_vgg16_fc7.h5
文件中和val2014_vgg16_fc7.h5
。
为了减少处理时间和内存需求,我们将特征的维数从4096降至512.处理后的特征可以在train2014_vgg16_fc7_pca.h5
文件和val2014_vgg16_fc7_pca.h5
中找到。
原始图像占用了大量空间(近20GB),所以我们没有在下载中包含它们。但是,所有的图像都是从Flickr上获取的,训练和验证图像的url分别存储在train2014_url .txt
和val2014_url .txt
文件中。这允许您动态下载图像以实现可视化。由于图像是动态下载的,您必须连接到internet才能查看图像。
处理字符串的效率很低,所以我们将使用字幕的编码版本。每个单词都分配了一个整数ID,允许我们用一个整数序列来表示标题。整数id和单词之间的映射在coco2014_vocab.json
文件中。可以使用文件cs231n/coco_utils.py
中的decode_captions
函数将整数id的numpy数组转换回字符串。
我们向词汇表中添加了一些特殊的标记。我们在每个标题的开头和结尾分别添加了一个特殊的 <START> token和一个 <END> token。罕见字被替换为一个特殊的token <UNK>(表示“未知”)。此外,由于我们希望使用包含不同长度字幕的小批量进行训练,所以对于那些短的字幕描述,我们在 <END> token 后面添加特殊的token <NULL>,并且我们不计算 <NULL> 的损失核梯度。由于它们有点麻烦,我们已经为您处理了关于特殊token的所有实现细节。
您可以从cs231n/coco_utils.py
文件中使用load_coco_data
函数加载所有MS-COCO数据(标题、特性、url和词汇表)。运行以下代码:
# Load COCO data from disk; this returns a dictionary
# We'll work with dimensionality-reduced features for this notebook, but feel
# free to experiment with the original features by changing the flag below.
data = load_coco_data(pca_features=True)
# Print out all the keys and values from the data dictionary
for k, v in data.items():
if type(v) == np.ndarray:
print(k, type(v), v.shape, v.dtype)
else:
print(k, type(v), len(v))
输出:
# val set
# caption 对应的 image 索引
val_image_idxs <class 'numpy.ndarray'> (195954,) int32
# images 特征
val_features <class 'numpy.ndarray'> (40504, 512) float32
# caption 索引以及,文本编码
val_captions <class 'numpy.ndarray'> (195954, 17) int32
# images 的下载连接
val_urls <class 'numpy.ndarray'> (40504,) <U63
# images 的下载连接
train_urls <class 'numpy.ndarray'> (82783,) <U63
# images 特征
train_features <class 'numpy.ndarray'> (82783, 512) float32
# captions索引以及对应的文本编码
train_captions <class 'numpy.ndarray'> (400135, 17) int32
# caption 对应的 image 索引
train_image_idxs <class 'numpy.ndarray'> (400135,) int32
# 单词以及编码的映射
word_to_idx <class 'dict'> 1004
idx_to_word <class 'list'> 1004
1.1 可视化数据
在使用数据集之前,从数据集查看示例总是一个好主意。
您可以使用cs231n/coco_utils.py
文件中的sample_coco_minibatch
函数,从load_coco_data
返回的数据结构中提取小批量数据。运行以下命令对一小批训练数据进行采样,并显示图像及其描述。多次运行它并查看结果可以帮助您了解数据集。
# sample_coco_minibatch 函数
def sample_coco_minibatch(data, batch_size=100, split='train'):
split_size = data['%s_captions' % split].shape[0]
mask = np.random.choice(split_size, batch_size)
captions = data['%s_captions' % split][mask]
image_idxs = data['%s_image_idxs' % split][mask]
image_features = data['%s_features' % split][image_idxs]
urls = data['%s_urls' % split][image_idxs]
return captions, image_features, urls
# 解码文本
def decode_captions(captions, idx_to_word):
singleton = False
if captions.ndim == 1:
singleton = True
captions = captions[None]
decoded = []
N, T = captions.shape
for i in range(N):
words = []
for t in range(T):
word = idx_to_word[captions[i, t]]
if word != '<NULL>':
words.append(word)
if word == '<END>':
break
decoded.append(' '.join(words))
if singleton:
decoded = decoded[0]
return decoded
# 从图像连接url中下载图像
import urllib.request, urllib.error, urllib.parse, os, tempfile
def image_from_url(url):
"""
Read an image from a URL. Returns a numpy array with the pixel data.
We write the image to a temporary file then read it back. Kinda gross.
"""
try:
f = urllib.request.urlopen(url)
_, fname = tempfile.mkstemp()
with open(fname, 'wb') as ff:
ff.write(f.read())
img = imread(fname)
# 这里会报错另一个程序正在使用此文件,进程无法访问。
# 所以,我加了一个异常跳过
try:
os.remove(fname)
except Exception as e: print(e)
return img
except urllib.error.URLError as e:
print('URL Error: ', e.reason, url)
except urllib.error.HTTPError as e:
print('HTTP Error: ', e.code, url)
显示一个mini-batch数据:
# Sample a minibatch and show the images and captions
batch_size = 3
captions, features, urls = sample_coco_minibatch(data, batch_size=batch_size)
for i, (caption, url) in enumerate(zip(captions, urls)):
plt.imshow(image_from_url(url))
plt.axis('off')
caption_str = decode_captions(caption, data['idx_to_word'])
plt.title(caption_str)
plt.show()
显示结果:
2. 递归神经网络(RNN)
正如在课堂上所讨论的,我们将使用递归神经网络(RNN)语言模型进行图像字幕。文件cs231n/rnn_layers.py
包含循环神经网络所需的不同层类型的实现,而文件cs231n/classifier /rnn.py
使用这些层实现一个图像字幕模型。
我们将首先在cs231n/rnn_layers.py
中实现不同类型的RNN层。
2.1 朴素RNN:单时间步的前向传播
打开文件cs231n/rnn_layers.py
。该文件实现了递归神经网络中常用的不同类型的层的前向和后向传递。
首先实现rnn_step_forward
函数,该函数实现了普通递归神经网络单时间步的前向传播。这样做之后,运行以下代码来检查您的实现。您应该看到e-8或更少的数量级上的错误。
# step forward
# h(t) = tanh(wh*h(t-1) + wx*x(t)+b)
def rnn_step_forward(x, prev_h, Wx, Wh, b):
"""
Run the forward pass for a single timestep of a vanilla RNN that uses a tanh
activation function.
The input data has dimension D, the hidden state has dimension H, and we use
a minibatch size of N.
Inputs:
- x: Input data for this timestep, of shape (N, D).
- prev_h: Hidden state from previous timestep, of shape (N, H)
- Wx: Weight matrix for input-to-hidden connections, of shape (D, H)
- Wh: Weight matrix for hidden-to-hidden connections, of shape (H, H)
- b: Biases of shape (H,)
Returns a tuple of:
- next_h: Next hidden state, of shape (N, H)
- cache: Tuple of values needed for the backward pass.
"""
next_h, cache = None, None
z = prev_h.dot(Wh) + x.dot(Wx) + b
next_h = np.tanh(z)
cache = (next_h,Wh,Wx,prev_h,x)
return next_h, cache
测试:
N, D, H = 3, 10, 4
x = np.linspace(-0.4, 0.7, num=N*D).reshape(N, D)
prev_h = np.linspace(-0.2, 0.5, num=N*H).reshape(N, H)
Wx = np.linspace(-0.1, 0.9, num=D*H).reshape(D, H