Yukyukuon / blog

博客的文章
1 stars 0 forks source link

Deep Learning with Python 3 #16

Open Yukyukuon opened 3 years ago

Yukyukuon commented 3 years ago

视频P10,书第三章

Training a neural network revolves around the following objects:

# Keras - It allows the same code to run seamlessly on CPU or GPU. - It has a user-friendly API. - It has built-in support for convolutional networks(for computer vision),recurrent networks(for sequence processing),and any combination of both. - It supports arbitrary network architectures:multi-input models,layer sharing,model sharing,and so on. 定义模型有两种方式:一种是使用Sequential类(仅用于层的线性堆叠,这是目前最常见的网络架构),另一种是函数式API(functional API, 用于层组成的有向无环图,让你可以构建任意新式的架构) ```Python # The Sequential class from keras import models from keras import layers model = models.Sequential() model.add(layers.Dense(32, activation = 'relu', input_shape=(784,))) model.add(layers.Dense(10, activation ='softmax')) ``` # 二分类问题 (IMDB影片预测) ### 基本知识 jupyter的线上检索,在查询的代码前加 **?** ``` Python # 如查询加载的IMDB数据库 ?imdb.load_data ``` 会显示如下 ``` Python imdb.load_data( path='imdb.npz', //储存路径 num_words=None, //选择最常用的多少字(如num_words=10000,选择10000个最常用的) skip_top=0, //跳过某些最长用的字 maxlen=None, //每个文章的长度选择多少 seed=113, start_char=1, // 1不代表任何数字,表示文章开始 oov_char=2, // 2代表的该字是超过所选择最常用的界限(如上面选择10000,表示超过前10000最常用的) index_from=3, **kwargs, ) ``` 评论解码为英文单词 ```Python word_index = imdb.get_word_index() //word_index是一个将字典映射成整数索引的字典 reverse_word_index = dict([(value, key) for (key, value)in word_index.items()]) //键值颠倒,将整数索引映射成单词 ##将评论解码,索引减去3,因为0,1,2是为"padding(填充)", 序列开始, 未知词 分别保留的索引 decoded_review = ' '.join([reverse_word_index.get(i - 3, '?') for i in train_data[0]]) ``` ## 数据准备 注:此处将列表转换为张量,是模糊影评,只是数出现了哪些字,不是记录每个字的出现次数 对列表进行 one-hot 编码,将其转换为 0 和 1 组成的向量。 ``` Python import numpy as np def vectorize_sequences(sequences, dimension=10000): results = np.zeros((len(sequences), dimension)) //创建零矩阵 for i, sequence in enumerate(sequences): results[i, sequence] = 1. //将results[i] 的指定索引设为1 return results # 调用自定义函数,数据初始化 x_train = vectorize_sequences(train_data) x_test = vectorize_sequences(test_data) # 标签初始化(整数数组转换成浮点) y_train = np.asarray(train_labels).astype('float32') y_test = np.asarray(test_labels).astype('float32') ``` ## 构建网络 ``` Python from keras import models from keras import layers model = models.Sequential() model.add(layers.Dense(16, activation='relu', input_shape=(10000,))) model.add(layers.Dense(16, activation='relu')) model.add(layers.Dense(1, activation='sigmoid')) ``` ## 编译模型,配置优化器 选择损失函数和优化器,下面代码是启用预设 ``` Python model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy']) ``` 也可以自定**optimizer,loss function,metrics**: ```Python from keras import optimizers from keras import losses from keras import metrics model.compile(optimizer=optimizers.RMSprop(lr=0.001), loss=losses.binary_crossentropy, metrics=[metrics.binary_accuracy]) ``` ## 留出验证集 将训练用数据集分割成test_data和val_data: ```Python #原始训练数据留出10 000个样本作为验证集 x_val = x_train[:10000] partial_x_train = x_train[10000:] y_val = y_train[:10000] partial_y_train = y_train[10000:] ``` ## 训练模型 ``` Python history = model.fit(partial_x_train, partial_y_train, epochs=20, batch_size=512, validation_data=(x_val, y_val)) ``` ## 绘制图像 调用model.fit() 返回了一个History 对象。对象有一个成员history,它是一个字典,包含训练过程中的所有数据。 ``` Python history_dict = history.history history_dict.keys() # 字典中包含4个条目:dict_keys(['val_acc', 'acc', 'val_loss', 'loss']) ``` 开始绘制训练损失和验证损失 ``` Python import matplotlib.pyplot as plt history_dict = history.history loss_values = history_dict['loss'] val_loss_values = history_dict['val_loss'] # 让epochs强制从1开始计数,到len+1 epochs = range(1, len(loss_values) + 1) # 画图:plt.plot(x轴,y轴,点线颜色定义,标签) # ‘bo’ is for "blue dot" # 'b' is for "solid blue line" plt.plot(epochs, loss_values, 'bo', label='Training loss') plt.plot(epochs, val_loss_values, 'b', label='Validation loss') # 设置图表,x轴,y轴的Title plt.title('Trainging and validation loss') plt.xlabel('Epochs') plt.ylabel('Loss') plt.legend() plt.show() ``` # 单标签,多元分类问题(Single-label, multi-class classification):路透社新闻分类 ## 数据准备 和上面的IMDB影评一样做one-hot编码 ### 训练集和测试集 做独热编码 ``` Python def vectorize_sequences(sequences, dimension=10000): results = np.zeros((len(sequences), dimension)) for i, sequence in enumerate(sequences): results[i, sequence] = 1. return results x_train = vectorize_sequences(train_data) x_test = vectorize_sequences(test_data) ``` ### 训练集标签和测试集标签 #### 做独热编码的方式 用自定义one-hot encoding ``` Python def to_one_hot(labels, dimension=46): results = np.zeros((len(labels), dimension)) for i,labels in enumerate(labels): results[i, labels] = 1. return results one_hot_train_labels = to_one_hot(train_labels) one_hot_test_labels = to_one_hot(test_labels) ``` 用keras内置函数可以替代: ``` Python from keras.utils.np_utils import to_categorical # 调用内置函数 one_hot_train_labels = to_categorical(train_labels) one_hot_test_labels = to_categorical(test_labels) ``` #### 不做独热编码的方式 可以对标签不做编码变换,但是此处loss function要换成**loss = 'sparse_categorical_crossentropy**,电脑会对标签自动做one-hot encoding ``` Python # 直接获取标签 y_train = np.array(train_labels) y_test = np.array(test_labels) model.compile(optimizer='rmsprop', loss='sparse_categorical_crossentropy', metrics=['acc']) ``` # 回归问题:预测房价 回归问题是预测一个连续值而不是离散的标签 > 不要将回归问题与logistic 回归算法混为一谈。令人困惑的是,logistic 回归不是回归算法, 而是分类算法。 ## 数据准备 ``` Python # 加载数据 from keras.datasets import boston_housing (train_data, train_targets), (test_data, test_targets) = boston_housing.load_data() ``` #### 数据标准化: 取值范围差异很大的数据输入,普遍采用的最佳实践是对每个特征做标准化, 即对于输入数据的每个特征(输入数据矩阵中的列),减去特征平均值,再除以标准差,这样得到的特征平均值为0,标准差为1。用Numpy 可以很容易实现标准化。 ``` Python # 计算平均值 mean = train_data.mean(axis=0) train_data -= mean # 计算标准差 std = train_data.std(axis=0) train_data /= std # 测试集做相同操作 test_data -= mean test_data /= std ``` ## 构建网络 网络的最后一层只有一个单元,没有激活,是一个线性层。这是标量回归(标量回归是预测单一连续值的回归)的典型设置。添加激活函数将会限制输出范围。 ``` Python from keras import models from keras import layers def build_model(): # Because we will need to instantiate # the same model multiple times, # we use a function to construct it. model = models.Sequential() model.add(layers.Dense(64, activation='relu', input_shape=(train_data.shape[1],))) model.add(layers.Dense(64, activation='relu')) model.add(layers.Dense(1)) model.compile(optimizer='rmsprop', loss='mse', metrics=['mae']) return model ``` #### MSE和MAE **均方误差**(MSE,mean squared error),预测值与目标值之差的平方。这是回归问题常用的损失函数。 **平均绝对误差**(MAE,mean absolute error)。它是预测值与目标值之差的绝对值。比如,如果这个问题的MAE 等于0.5,就表示你预测的房价与实际价格平均相差500 美元。 ## K折交叉验证 当训练的数据量不够大的时候可以使用。 这种方法将可用数据划分为K个分区(K 通常取4 或5),实例化K 个相同的模型,将每个模型在K-1 个分区上训练,并在剩下的一个分区上进行评估。模型的验证分数等于K 个验证分数的平均值。 ``` Python import numpy as np k = 4 # 计算每块大小 (//为取整) num_val_samples = len(train_data) // k num_epochs = 100 all_scores = [] # k折验证 for i in range(k): print('processing fold #', i) # 分割验证数据集和验证标签: [i*每块大小 : (i+1)*每块大小] val_data = train_data[i * num_val_samples: (i + 1) * num_val_samples] val_targets = train_targets[i * num_val_samples: (i + 1) * num_val_samples] # 粘合被分割的训练数据集和标签(根据第一个轴粘,axis=0) partial_train_data = np.concatenate( [train_data[:i * num_val_samples], train_data[(i + 1) * num_val_samples:]], axis=0) partial_train_targets = np.concatenate( [train_targets[:i * num_val_samples], train_targets[(i + 1) * num_val_samples:]], axis=0) # 构筑已编译的模型 model = build_model() # 训练模型 model.fit(partial_train_data, partial_train_targets, epochs=num_epochs, batch_size=1, verbose=2) # 记录每次在验证数据上评估模型 val_mse, val_mae = model.evaluate(val_data, val_targets, verbose=2) # mae放入空空的all_scores[] all_scores.append(val_mae) # 查看运行结果 >>> all_scores [2.0662810802459717, 2.8503870964050293, 2.762800455093384, 2.3956613540649414] >>> np.mean(all_scores) 2.5187824964523315 ``` 接下来试试500个epochs的情况 ``` Python num_epochs = 500 all_mae_histories = [] for i in range(k): print('processing flod #', i) val_data = train_data[i * num_val_samples: (i + 1) * num_val_samples] val_targets = train_targets[i * num_val_samples: (i + 1) * num_val_samples] partial_train_data = np.concatenate( [train_data[:i * num_val_samples], train_data[(i +1 ) * num_val_samples:]], axis = 0) partial_train_taegets = np.concatenate( [train_targets[:i * num_val_samples], train_targets[(i + 1) * num_val_samples:]], axis=0) model = build_model() history = model.fit(partial_train_data, partial_train_targets, validation_data=(val_data, val_targets), epochs=num_epochs, batch_size=1, verbose=2) mae_history = history.history['val_mae'] all_mae_histories.append(mae_history) ``` ## 绘图 去掉前10个数据,并做指数平滑 ``` Python average_mae_history = [ np.mean([x[i] for x in all_mae_histories]) for i in range(num_epochs)] import matplotlib.pyplot as plt def smooth_curve(points, factor=0.9): smoothed_points = [] for point in points: if smoothed_points: previous = smoothed_points[-1] smoothed_points.append(previous * factor + point * (1 - factor)) else: smoothed_points.append(point) return smoothed_points smooth_mae_history = smooth_curve(average_mae_history[10:]) plt.plot(range(1, len(smooth_mae_history) + 1), smooth_mae_history) plt.xlabel('Epochs') plt.ylabel('Validation MAE') plt.show() ``` # 总结 #### 二元分类 - 通常需要对原始数据进行大量预处理,以便将其转换为张量输入到神经网络中。单词序列可以编码为二进制向量,但也有其他编码方式。 - 对于二分类问题(两个输出类别),网络的最后一层应该是只有一个单元并使用sigmoid激活的Dense 层,网络输出应该是0~1 范围内的标量,表示概率值。 - 对于二分类问题的 sigmoid标量输出,你应该使用binary_crossentropy损失函数。 #### 多元分类 - 如果要对 N个类别的数据点进行分类,网络的最后一层应该是大小为N的Dense层。 - 对于单标签、多分类问题,网络的最后一层应该使用softmax 激活,这样可以输出在N个输出类别上的概率分布。 - 这种问题的损失函数几乎总是应该使用分类交叉熵。(loss='categorical_crossentropy')它将网络输出的概率分布与目标的真实分布之间的距离最小化。 - 处理多分类问题的标签有两种方法。 - 通过分类编码(也叫 one-hot 编码)对标签进行编码,然后使用 categorical_crossentropy 作为损失函数。 - 将标签编码为整数,然后使用 sparse_categorical_crossentropy损失函数。 - 如果你需要将数据划分到许多类别中,应该避免使用太小的中间层,以免在网络中造成信息瓶颈。 #### 回归问题 - 回归问题使用的损失函数与分类问题不同。回归常用的损失函数是均方误差(MSE)。 - 回归问题使用的损失函数与分类问题不同。回归常用的损失函数是均方误差(MSE)。 - 如果输入数据的特征具有不同的取值范围,应该先进行预处理,对每个特征单独进行缩放。 - 如果可用的数据很少,使用 K折验证可以可靠地评估模型。 - 如果可用的训练数据很少,最好使用隐藏层较少(通常只有一到两个)的小型网络,以避免严重的过拟合。