赞
踩
本篇目录
二、 回归问题:使用Python和NumPy实现波士顿房价预测任务
6.2.1 LSTM (Long Short-Term Memory)介绍
8.1.1 一些本机CPU可运行的飞桨预训练简单模型(亲测可用)
简而言之:神经网络模型是拟合现实问题的函数方程,通过输入得到输出。
只不过这个函数是用神经网络的参数来拟合的,神经网络的参数是通过大量数据的训练获得,训练效果越好,则函数越逼近现实情况,就可以用来解决各种实际任务。
机器学习最根本的目的在于训练出在某个问题上泛化能力强的模型。泛化能力强是指在某问题的所有数据上都能很好地反应输入和输出之间的关系,无论是训练数据,还是测试数据,还是任何属于该问题的未知数据
神经网络经常处理的问题包括:回归问题,图像分类问题,目标检测问题,自然语言处理,喜好推荐等等。
一个简单的网络模型函数如下:
y=w1*x1 + w2*x2 + w3*x3...
其中,y是函数值(模型输出的预测值),x1,x2, x3...是输入值,w1, w2, w3...是网络参数。
神经网络模型预测数据中比较常见的是回归问题,根据输入的数值得到输出的数值。使用Python来实现波士顿房价预测是AI课程里类似“hello world”的经典入门案例,主要有以下一些要点需注意:
1. 样本数据需要归一化,使得后续的神经网络模型参数可表征有效的权重。样本数据归一化是以列(特征值)为单位的。注意,在用测试集测试模型时,模型输出的函数预测值需要进行反归一化。
2. 数据集划分:80%用于训练,20%用于测试,训练和测试数据集必须分开,才能验证网络的有效性。
3. 影响波士顿房价的样本数据有13个特征值,每个特征值会有不同的权重,因此神经网络模型的可调参数为13个,分别代表不同特征值对最终房价影响的权重:y=w1*x1 + w2*x2 + ... +w13*x13
4. 损失函数是模型输出的值与样本数据中实际值偏差的一种表达函数,损失函数的选择既要考虑准确衡量问题的“合理性”,也还要考虑“易于优化求解”。
5. 训练过程就是通过不断调整网络模型参数,将损失函数的值降到最小(收敛)的过程, 损失函数的收敛需要通过梯度下降法来不断训练。以房价预测任务为例,影响房价的特征值有13个,因此我们需要调教的模型参数也是13个,这13个特征值和损失函数的值共同构成一个14维的空间,在这个空间中存在一个方向(13个参数构成向量决定这个方向)使得损失函数的值(预测值和实际值之偏差)下降最快。我们步进地将13个参数构成的向量朝此方向做出微调,再重新计算损失函数的值,如此往复,直到损失函数的值收敛趋于最小,则参数训练完成。
6. 数据集采用分批训练的方式,batch的取值会影响模型训练效果,batch过大,会增大内存消耗和计算时间,且训练效果并不会明显提升(每次参数只向梯度反方向移动一小步,因此方向没必要特别精确);batch过小,每个batch的样本数据没有统计意义,计算的梯度方向可能偏差较大。由于房价预测模型的训练数据集较小,因此将batch设置为10
Python源码 - 波士顿房价模型训练及测试:
- # 导入需要用到的package
- import numpy as np
- import json
- import matplotlib.pyplot as plt
-
- def load_data():
- # 从文件导入数据
- datafile = './work/housing.data'
- data = np.fromfile(datafile, sep=' ')
-
- # 每条数据包括14项,其中前面13项是影响因素,第14项是相应的房屋价格中位数
- feature_names = [ 'CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', \
- 'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV' ]
- feature_num = len(feature_names)
-
- # 将原始数据进行Reshape,变成[N, 14]这样的形状
- data = data.reshape([data.shape[0] // feature_num, feature_num])
-
- # 将原数据集拆分成训练集和测试集
- # 这里使用80%的数据做训练,20%的数据做测试
- # 测试集和训练集必须是没有交集的
- ratio = 0.8
- offset = int(data.shape[0] * ratio)
- training_data = data[:offset]
-
- # 计算训练集的最大值,最小值(找的是每一列的极值)
- global maximums, minimums
- #maximums, minimums = data.max(axis=0), data.min(axis=0)
- maximums, minimums = training_data.max(axis=0), training_data.min(axis=0)
- #print("max:", maximums, "min:", minimums)
-
- # 对数据进行归一化处理,按列归一化处理
- for i in range(feature_num):
- data[:, i] = (data[:, i] - minimums[i]) / (maximums[i] - minimums[i])
- #print("归一化后的数据:\n", data)
-
- # 训练集和测试集的划分比例
- training_data = data[:offset]
- test_data = data[offset:]
- return training_data, test_data
-
- # 获取数据
- training_data, test_data = load_data()
- x = training_data[:, :-1] #所有行+所有列(除了最后一列)
- y = training_data[:, -1:] #所有行+最后一列
-
- #w = [1, 2, 3] #shape = (3,)
- #w = [[1], [2], [3]] #shape = (3,1)
- #w = [[1,1], [2,2], [3,3]] #shape = (3,2)
- #x = np.array(w)
- # 查看数据
- #print(x.shape)
- #print(y.shape)
-
- class Network(object):
- def __init__(self, num_of_weights):
- # 随机产生w的初始值
- # 为了保持程序每次运行结果的一致性,
- # 此处设置固定的随机数种子
- np.random.seed(0)
- self.w = np.random.randn(num_of_weights, 1)
- #print("init self.w", self.w)
- self.b = 0.
-
- def forward(self, x):
- z = np.dot(x, self.w) + self.b #x是[404,13]的矩阵(404行,13列), w是[13, 1]的矩阵(13行,1列),做点乘
- return z
-
- def loss(self, z, y):
- error = z - y
- #print(error.shape)
- cost = error * error
- cost = np.mean(cost)
- return cost
-
- def gradient(self, x, y):
- z = self.forward(x)
- gradient_w = (z-y)*x #梯度公式
- gradient_w = np.mean(gradient_w, axis=0) #对各列求均值
- gradient_w = gradient_w[:, np.newaxis]
- gradient_b = (z - y)
- gradient_b = np.mean(gradient_b)
- return gradient_w, gradient_b
-
- def update(self, gradient_w, gradient_b, eta = 0.01):
- self.w = self.w - eta * gradient_w
- self.b = self.b - eta * gradient_b
-
- def train(self, x, y, iterations=100, eta=0.01):
- losses = []
- for i in range(iterations):
- z = self.forward(x)
- L = self.loss(z, y)
- gradient_w, gradient_b = self.gradient(x, y)
- self.update(gradient_w, gradient_b, eta)
- losses.append(L)
- if (i+1) % 10000 == 0:
- print('iter {}, loss {}'.format(i, L))
- return losses
-
- # 运行模式一:每次用所有数据进行训练
- train_data, test_data = load_data()
- x = train_data[:, :-1]
- #print("x.shape:", x.shape)
- y = train_data[:, -1:]
- # 创建网络
- net = Network(13)
- num_iterations=100000
- # 启动训练
- losses = net.train(x,y, iterations=num_iterations, eta=0.01)
-
- # 画出损失函数的变化趋势
- """
- plot_x = np.arange(num_iterations)
- plot_y = np.array(losses)
- plt.plot(plot_x, plot_y)
- plt.show()
- """
-
- #对数据做反归一化处理
- def restore_data(d):
- d = d* (maximums[-1] - minimums[-1]) + minimums[-1]
- return round(d,2)
-
- #用测试集做测试
- print("测试集测试结果:")
- x = test_data[:, :-1]
- y = test_data[:, -1:]
- z = net.forward(x)
- print("样本数据", "\t", "预测数据")
- print("-------------------------")
- for i in range(x.shape[0]):
- print(restore_data(y[i][0]), "\t\t", restore_data(z[i][0]))
-
-
-
(输入层) --w--> (输出层)
(输入层) --w--> (隐含层) --w--> (隐含层) ... --> (输出层)
单层网络和多层网络默认只能表达线性变换,加入非线性激活函数后,可以表达非线性函数:
(输入层) --w--> (隐含层) --> (激活函数) --w--> (隐含层) --> (激活函数) ... --> (输出层)
加入非线性激励函数后,神经网络就有可能学习到平滑的曲线来分割平面,而不是用复杂的线性组合逼近平滑曲线来分割平面,使神经网络的表示能力更强了,能够更好的拟合目标函数。 这就是为什么我们要有非线性的激活函数的原因。
关于激活函数,可以参考:卷积神经网络中的激活函数sigmoid、tanh、relu_卷积神经网络激活函数_chaiky的博客-CSDN博客
手写数字识别是典型的图像分类任务(0~9个数字一共是10个类别)。我们首先尝试使用典型的全连接神经网络。
经典的全连接神经网络来包含四层网络:输入层、两个隐含层和输出层,将手写数字识别任务通过全连接神经网络表示:
Python源码 - 激活函数为sigmoid的多层网络参考代码:
- import paddle.nn.functional as F
- from paddle.nn import Linear
-
- # 定义多层全连接神经网络
- class MNIST(paddle.nn.Layer):
- def __init__(self):
- super(MNIST, self).__init__()
- # 定义两层全连接隐含层,输出维度是10,当前设定隐含节点数为10,可根据任务调整
- self.fc1 = Linear(in_features=784, out_features=10)
- self.fc2 = Linear(in_features=10, out_features=10)
- # 定义一层全连接输出层,输出维度是1
- self.fc3 = Linear(in_features=10, out_features=1)
-
- # 定义网络的前向计算,隐含层激活函数为sigmoid,输出层不使用激活函数
- def forward(self, inputs):
- # inputs = paddle.reshape(inputs, [inputs.shape[0], 784])
- outputs1 = self.fc1(inputs)
- outputs1 = F.sigmoid(outputs1)
- outputs2 = self.fc2(outputs1)
- outputs2 = F.sigmoid(outputs2)
- outputs_final = self.fc3(outputs2)
- return outputs_final
然而,全连接神经网络模型并不适合图像分类模型,图像分类任务需要考虑图像数据的空间性,以及如何分类(波士顿房价预测是回归任务,是回归到一个具体数字,手写数字识别实际上是进行分类判断),对于图像识别和分类任务,我们需要引入卷积神经网络,Softmax激活函数以及交叉熵损失函数,整个流程如下图:
图像识别需要考虑数据的空间分布,更适合使用卷积神经网络模型,模型中包含卷积层(convolution)和池化层(subsampling),以及最后一个全连接层(fully connected)
关于卷积神经网络,可以参考这一篇:
PyTorch学习系列教程:卷积神经网络【CNN】 - 知乎
关于卷积核和输入,输出通道,可以参考这一篇:
如何理解卷积神经网络中的通道(channel)_卷积通道数_叹久01的博客-CSDN博客
Python源码 - 卷积神经网络参考代码:
- # 定义 SimpleNet 网络结构
- import paddle
- from paddle.nn import Conv2D, MaxPool2D, Linear
- import paddle.nn.functional as F
- # 多层卷积神经网络实现
- class MNIST(paddle.nn.Layer):
- def __init__(self):
- super(MNIST, self).__init__()
-
- # 定义卷积层,输出特征通道out_channels设置为20,卷积核的大小kernel_size为5,卷积步长stride=1,padding=2
- self.conv1 = Conv2D(in_channels=1, out_channels=20, kernel_size=5, stride=1, padding=2)
- # 定义池化层,池化核的大小kernel_size为2,池化步长为2
- self.max_pool1 = MaxPool2D(kernel_size=2, stride=2)
- # 定义卷积层,输出特征通道out_channels设置为20,卷积核的大小kernel_size为5,卷积步长stride=1,padding=2
- self.conv2 = Conv2D(in_channels=20, out_channels=20, kernel_size=5, stride=1, padding=2)
- # 定义池化层,池化核的大小kernel_size为2,池化步长为2
- self.max_pool2 = MaxPool2D(kernel_size=2, stride=2)
- # 定义一层全连接层,输出维度是1
- self.fc = Linear(in_features=980, out_features=1)
-
- # 定义网络前向计算过程,卷积后紧接着使用池化层,最后使用全连接层计算最终输出
- # 卷积层激活函数使用Relu,全连接层不使用激活函数
- def forward(self, inputs):
- x = self.conv1(inputs)
- x = F.relu(x)
- x = self.max_pool1(x)
- x = self.conv2(x)
- x = F.relu(x)
- x = self.max_pool2(x)
- x = paddle.reshape(x, [x.shape[0], -1])
- x = self.fc(x)
- return x
为了进行分类判别,要通过引入Softmax函数到输出层,使得输出层的输出为不同类别概率的集合,并且所有概率之和为1,比如[0.1, 0.2, 0.7]
比如,一个三个标签的分类模型(三分类)使用的Softmax输出层,从中可见原始输出的三个数字3、1、-3,经过Softmax层后转变成加和为1的三个概率值0.88、0.12、0。
分类网络模型需要使用交叉熵损失函数不断训练更新模型参数,最终使得交叉熵趋于收敛,从而完成模型训练。
正确解标签对应的输出越大,交叉熵的值越接近0;当输出为1时,交叉熵误差为0。反之,如果正确解标签对应的输出越小,则交叉熵的值越大。
要想搞清楚交叉熵,推荐大家读一下这篇文章:损失函数:交叉熵详解 - 知乎
里面又牵涉到极大似然估计理论,推荐阅读这篇文章:极大似然估计思想的最简单解释_class_brick的博客-CSDN博客
学习率是优化器的一个参数,调整学习率看似是一件非常麻烦的事情,需要不断的调整步长,观察训练时间和Loss的变化。经过研究员的不断的实验,当前已经形成了四种比较成熟的优化算法:SGD、Momentum、AdaGrad和Adam,效果如 图所示。
图3: 不同学习率算法效果示意图
在计算机视觉中,通常会对图像做一些随机的变化,产生相似但又不完全相同的样本。主要作用是扩大训练数据集,抑制过拟合,提升模型的泛化能力,常用的方法主要有以下几种:
下面是分别使用numpy 实现这些数据增强方法。
- import numpy as np
- import cv2
- from PIL import Image, ImageEnhance
- import random
-
- # 随机改变亮暗、对比度和颜色等
- def random_distort(img):
- # 随机改变亮度
- def random_brightness(img, lower=0.5, upper=1.5):
- e = np.random.uniform(lower, upper)
- return ImageEnhance.Brightness(img).enhance(e)
- # 随机改变对比度
- def random_contrast(img, lower=0.5, upper=1.5):
- e = np.random.uniform(lower, upper)
- return ImageEnhance.Contrast(img).enhance(e)
- # 随机改变颜色
- def random_color(img, lower=0.5, upper=1.5):
- e = np.random.uniform(lower, upper)
- return ImageEnhance.Color(img).enhance(e)
-
- ops = [random_brightness, random_contrast, random_color]
- np.random.shuffle(ops)
-
- img = Image.fromarray(img)
- img = ops[0](img)
- img = ops[1](img)
- img = ops[2](img)
- img = np.asarray(img)
-
- return img
-
- # 定义可视化函数,用于对比原图和图像增强的效果
- import matplotlib.pyplot as plt
- def visualize(srcimg, img_enhance):
- # 图像可视化
- plt.figure(num=2, figsize=(6,12))
- plt.subplot(1,2,1)
- plt.title('Src Image', color='#0000FF')
- plt.axis('off') # 不显示坐标轴
- plt.imshow(srcimg) # 显示原图片
-
- # 对原图做 随机改变亮暗、对比度和颜色等 数据增强
- srcimg_gtbox = records[0]['gt_bbox']
- srcimg_label = records[0]['gt_class']
-
- plt.subplot(1,2,2)
- plt.title('Enhance Image', color='#0000FF')
- plt.axis('off') # 不显示坐标轴
- plt.imshow(img_enhance)
-
-
- image_path = records[0]['im_file']
- print("read image from file {}".format(image_path))
- srcimg = Image.open(image_path)
- # 将PIL读取的图像转换成array类型
- srcimg = np.array(srcimg)
-
- # 对原图做 随机改变亮暗、对比度和颜色等 数据增强
- img_enhance = random_distort(srcimg)
- visualize(srcimg, img_enhance)
- # 随机填充
- def random_expand(img,
- gtboxes,
- max_ratio=4.,
- fill=None,
- keep_ratio=True,
- thresh=0.5):
- if random.random() > thresh:
- return img, gtboxes
-
- if max_ratio < 1.0:
- return img, gtboxes
-
- h, w, c = img.shape
- ratio_x = random.uniform(1, max_ratio)
- if keep_ratio:
- ratio_y = ratio_x
- else:
- ratio_y = random.uniform(1, max_ratio)
- oh = int(h * ratio_y)
- ow = int(w * ratio_x)
- off_x = random.randint(0, ow - w)
- off_y = random.randint(0, oh - h)
-
- out_img = np.zeros((oh, ow, c))
- if fill and len(fill) == c:
- for i in range(c):
- out_img[:, :, i] = fill[i] * 255.0
-
- out_img[off_y:off_y + h, off_x:off_x + w, :] = img
- gtboxes[:, 0] = ((gtboxes[:, 0] * w) + off_x) / float(ow)
- gtboxes[:, 1] = ((gtboxes[:, 1] * h) + off_y) / float(oh)
- gtboxes[:, 2] = gtboxes[:, 2] / ratio_x
- gtboxes[:, 3] = gtboxes[:, 3] / ratio_y
-
- return out_img.astype('uint8'), gtboxes
-
-
- # 对原图做 随机改变亮暗、对比度和颜色等 数据增强
- srcimg_gtbox = records[0]['gt_bbox']
- img_enhance, new_gtbox = random_expand(srcimg, srcimg_gtbox)
- visualize(srcimg, img_enhance)
随机裁剪之前需要先定义两个函数,multi_box_iou_xywh
和box_crop
这两个函数将被保存在box_utils.py文件中。
- import numpy as np
-
- def multi_box_iou_xywh(box1, box2):
- """
- In this case, box1 or box2 can contain multi boxes.
- Only two cases can be processed in this method:
- 1, box1 and box2 have the same shape, box1.shape == box2.shape
- 2, either box1 or box2 contains only one box, len(box1) == 1 or len(box2) == 1
- If the shape of box1 and box2 does not match, and both of them contain multi boxes, it will be wrong.
- """
- assert box1.shape[-1] == 4, "Box1 shape[-1] should be 4."
- assert box2.shape[-1] == 4, "Box2 shape[-1] should be 4."
-
-
- b1_x1, b1_x2 = box1[:, 0] - box1[:, 2] / 2, box1[:, 0] + box1[:, 2] / 2
- b1_y1, b1_y2 = box1[:, 1] - box1[:, 3] / 2, box1[:, 1] + box1[:, 3] / 2
- b2_x1, b2_x2 = box2[:, 0] - box2[:, 2] / 2, box2[:, 0] + box2[:, 2] / 2
- b2_y1, b2_y2 = box2[:, 1] - box2[:, 3] / 2, box2[:, 1] + box2[:, 3] / 2
-
- inter_x1 = np.maximum(b1_x1, b2_x1)
- inter_x2 = np.minimum(b1_x2, b2_x2)
- inter_y1 = np.maximum(b1_y1, b2_y1)
- inter_y2 = np.minimum(b1_y2, b2_y2)
- inter_w = inter_x2 - inter_x1
- inter_h = inter_y2 - inter_y1
- inter_w = np.clip(inter_w, a_min=0., a_max=None)
- inter_h = np.clip(inter_h, a_min=0., a_max=None)
-
- inter_area = inter_w * inter_h
- b1_area = (b1_x2 - b1_x1) * (b1_y2 - b1_y1)
- b2_area = (b2_x2 - b2_x1) * (b2_y2 - b2_y1)
-
- return inter_area / (b1_area + b2_area - inter_area)
-
- def box_crop(boxes, labels, crop, img_shape):
- x, y, w, h = map(float, crop)
- im_w, im_h = map(float, img_shape)
-
- boxes = boxes.copy()
- boxes[:, 0], boxes[:, 2] = (boxes[:, 0] - boxes[:, 2] / 2) * im_w, (
- boxes[:, 0] + boxes[:, 2] / 2) * im_w
- boxes[:, 1], boxes[:, 3] = (boxes[:, 1] - boxes[:, 3] / 2) * im_h, (
- boxes[:, 1] + boxes[:, 3] / 2) * im_h
-
- crop_box = np.array([x, y, x + w, y + h])
- centers = (boxes[:, :2] + boxes[:, 2:]) / 2.0
- mask = np.logical_and(crop_box[:2] <= centers, centers <= crop_box[2:]).all(
- axis=1)
-
- boxes[:, :2] = np.maximum(boxes[:, :2], crop_box[:2])
- boxes[:, 2:] = np.minimum(boxes[:, 2:], crop_box[2:])
- boxes[:, :2] -= crop_box[:2]
- boxes[:, 2:] -= crop_box[:2]
-
- mask = np.logical_and(mask, (boxes[:, :2] < boxes[:, 2:]).all(axis=1))
- boxes = boxes * np.expand_dims(mask.astype('float32'), axis=1)
- labels = labels * mask.astype('float32')
- boxes[:, 0], boxes[:, 2] = (boxes[:, 0] + boxes[:, 2]) / 2 / w, (
- boxes[:, 2] - boxes[:, 0]) / w
- boxes[:, 1], boxes[:, 3] = (boxes[:, 1] + boxes[:, 3]) / 2 / h, (
- boxes[:, 3] - boxes[:, 1]) / h
-
- return boxes, labels, mask.sum()
-
- # 随机裁剪
- def random_crop(img,
- boxes,
- labels,
- scales=[0.3, 1.0],
- max_ratio=2.0,
- constraints=None,
- max_trial=50):
- if len(boxes) == 0:
- return img, boxes
-
- if not constraints:
- constraints = [(0.1, 1.0), (0.3, 1.0), (0.5, 1.0), (0.7, 1.0),
- (0.9, 1.0), (0.0, 1.0)]
-
- img = Image.fromarray(img)
- w, h = img.size
- crops = [(0, 0, w, h)]
- for min_iou, max_iou in constraints:
- for _ in range(max_trial):
- scale = random.uniform(scales[0], scales[1])
- aspect_ratio = random.uniform(max(1 / max_ratio, scale * scale), \
- min(max_ratio, 1 / scale / scale))
- crop_h = int(h * scale / np.sqrt(aspect_ratio))
- crop_w = int(w * scale * np.sqrt(aspect_ratio))
- crop_x = random.randrange(w - crop_w)
- crop_y = random.randrange(h - crop_h)
- crop_box = np.array([[(crop_x + crop_w / 2.0) / w,
- (crop_y + crop_h / 2.0) / h,
- crop_w / float(w), crop_h / float(h)]])
-
- iou = multi_box_iou_xywh(crop_box, boxes)
- if min_iou <= iou.min() and max_iou >= iou.max():
- crops.append((crop_x, crop_y, crop_w, crop_h))
- break
-
- while crops:
- crop = crops.pop(np.random.randint(0, len(crops)))
- crop_boxes, crop_labels, box_num = box_crop(boxes, labels, crop, (w, h))
- if box_num < 1:
- continue
- img = img.crop((crop[0], crop[1], crop[0] + crop[2],
- crop[1] + crop[3])).resize(img.size, Image.LANCZOS)
- img = np.asarray(img)
- return img, crop_boxes, crop_labels
- img = np.asarray(img)
- return img, boxes, labels
-
-
- # 对原图做 随机改变亮暗、对比度和颜色等 数据增强
- srcimg_gtbox = records[0]['gt_bbox']
- srcimg_label = records[0]['gt_class']
-
- img_enhance, new_labels, mask = random_crop(srcimg, srcimg_gtbox, srcimg_label)
- visualize(srcimg, img_enhance)
-
- # 随机缩放
- def random_interp(img, size, interp=None):
- interp_method = [
- cv2.INTER_NEAREST,
- cv2.INTER_LINEAR,
- cv2.INTER_AREA,
- cv2.INTER_CUBIC,
- cv2.INTER_LANCZOS4,
- ]
- if not interp or interp not in interp_method:
- interp = interp_method[random.randint(0, len(interp_method) - 1)]
- h, w, _ = img.shape
- im_scale_x = size / float(w)
- im_scale_y = size / float(h)
- img = cv2.resize(
- img, None, None, fx=im_scale_x, fy=im_scale_y, interpolation=interp)
- return img
-
- # 对原图做 随机缩放
- img_enhance = random_interp(srcimg, 640)
- visualize(srcimg, img_enhance)
- # 随机翻转
- def random_flip(img, gtboxes, thresh=0.5):
- if random.random() > thresh:
- img = img[:, ::-1, :]
- gtboxes[:, 0] = 1.0 - gtboxes[:, 0]
- return img, gtboxes
-
-
- # 对原图做 随机改变亮暗、对比度和颜色等 数据增强
- img_enhance, box_enhance = random_flip(srcimg, srcimg_gtbox)
- visualize(srcimg, img_enhance)
- # 随机打乱真实框排列顺序
- def shuffle_gtbox(gtbox, gtlabel):
- gt = np.concatenate(
- [gtbox, gtlabel[:, np.newaxis]], axis=1)
- idx = np.arange(gt.shape[0])
- np.random.shuffle(idx)
- gt = gt[idx, :]
- return gt[:, :4], gt[:, 4]
对计算机而言,能够“看到”的是图像被编码之后的数字,但它很难理解高层语义概念,比如图像或者视频帧中出现的目标是人还是物体,更无法定位目标出现在图像中哪个区域。目标检测的主要目的是让计算机可以自动识别
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。