API优先设计:构建可扩展且灵活的软件的现代方法
利用机器学习进行时尚图像识别
导言
计算机可以自动检测衬衫、裤子、裙子和运动鞋的图片吗?事实证明,只要有高质量的训练数据作为基础,准确地对时尚物品图片进行分类是一件非常容易的事情。
监督学习,尤其是分类学习,是人工智能和机器学习爱好者的热门话题。开发人员在首次尝试使用监督学习时,通常会利用一个众所周知且易于处理的数据集。 MNIST 数据集就是这样一个例子,它提供了数千个手写数字示例,可用于机器学习算法的监督学习。
重要的是要记住,好的数据集有几个共同特点。首先,它包含成千上万个训练数据示例。事实上,越多越好。 这样,您就可以利用大量示例建立训练集和交叉验证集,以便在人工智能和机器学习模型中使用。其次,数据集包含所有示例的一致特征集。 在 MNIST 数据集中,数据集内每个手写数字示例都以 28×28 像素灰度图像的形式提供。这样就可以轻松建立机器学习模型,并在训练时专注于参数。
与 MNIST 手写数据集一样, 时尚数据集 也包含相同的图像尺寸和特征集。
在本教程中,我们将逐步建立一个用于识别时尚物品图像的机器学习模型。与手写数字数据集一样,时尚数据集由数千个 28×28 灰度图像实例组成,分为 10 个不同类别。时尚图像被分为 10 个类别,而不是将手写数字分为 0-9 类。 我们将介绍如何训练模型、设计类别分类的输入和输出,最后显示每个模型的准确性结果。
由于时装训练集相当庞大(60,000 个训练示例和另外 10,000 个测试集),我们将只对数据的一个子集进行训练。 即使使用较小的训练集,我们也会向您展示,您仍然可以通过机器学习模型取得令人印象深刻的结果。
利用机器学习和图像识别技术对时装进行分类
时尚-MNIST 数据集
时尚-MNIST 数据集是仿照 MNIST 数据集建模的,目的是提供最简单快捷的建模途径。如果您已经熟悉 MNIST 手写数字数据集,这一点尤为重要。 事实上,在加载时尚数据时,可以使用与加载 MNIST 数据相同的代码。
与手写数字数据集一样,时尚数据集由 10 个类别组成。这些类别如下所示。
标签 | 说明 |
0 | T 恤/上衣 |
1 | 裤装 |
2 | 套头衫 |
3 | 连衣裙 |
4 | 外套 |
5 | 凉鞋 |
6 | 衬衫 |
7 | 运动鞋 |
8 | 袋子 |
9 | 踝靴 |
可以使用与 MNIST 数据集 相同的代码 下载和加载该数据集。代码示例可在 多种 编程语言中找到,但本教程将使用 R 语言。
加载时尚数据集
以下代码可用于加载时装数据集 文件 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | load_mnist <- function() { load_image_file <- function(filename) { ret = list() f = gzfile(filename,’rb’) readBin(f,’integer’,n=1,size=4,endian=’big’) ret$n = readBin(f,’integer’,n=1,size=4,endian=’big’) nrow = readBin(f,’integer’,n=1,size=4,endian=’big’) ncol = readBin(f,’integer’,n=1,size=4,endian=’big’) x = readBin(f,’integer’,n=ret$n*nrow*ncol,size=1,signed=F) ret$x = matrix(x, ncol=nrow*ncol, byrow=T) close(f) ret } load_label_file <- function(filename) { f = gzfile(filename,’rb’) readBin(f,’integer’,n=1,size=4,endian=’big’) n = readBin(f,’integer’,n=1,size=4,endian=’big’) y = readBin(f,’integer’,n=n,size=1,signed=F) close(f) y } trainData <<- load_image_file(‘data/train-images-idx3-ubyte.gz’) testData <<- load_image_file(‘data/t10k-images-idx3-ubyte.gz’) trainData$y <<- load_label_file(‘data/train-labels-idx1-ubyte.gz’) testData$y <<- load_label_file(‘data/t10k-labels-idx1-ubyte.gz’) } show_digit <- function(arr784, col=gray(12:1/12), …) { image(matrix(arr784, nrow=28)[,28:1], col=col, …) } |
上述代码读取时尚数据集文件。共有 4 个文件,分别对应训练图像集、相关标签以及测试图像集和相关标签。您可以 下载 这些文件,并将它们放在 /data
目录中以便加载。
1 2 3 4 5 6 7 8 9 | 60,000 training images: t10k-images-idx3-ubyte.gz t10k-labels-idx1-ubyte.gz 10,000 test images: train-images-idx3-ubyte.gz train-labels-idx1-ubyte.gz |
请注意,同样的代码也可用于加载 MNIST 手写数字数据集!
最后,要执行加载图像的调用,只需运行以下命令即可:
1 2 | load_mnist() |
检查数据
现在我们已经加载了图像数据,让我们来看看它的内容。图像数据已加载到一个名为 trainData
的变量中。这个变量包含三个部分
1 2 3 | trainData$n – the number of records that were loaded (60,000). trainData$y – the label for each image (0-9), representing a fashion category. trainData$x – a matrix of 28×28 pixel images, each record is an array of 784 integers for each pixel in the image. |
检查像素
让我们检查第一幅图像。我们可以运行以下命令查看第一幅图像的整数数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | trainData$x[1,] [1] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [21] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [41] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [61] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [81] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 13 [101] 73 0 0 1 4 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 [121] 0 0 0 0 3 0 36 136 127 62 54 0 0 0 1 3 4 0 0 3 [141] 0 0 0 0 ] 0 0 155 236 207 178 107 156 161 109 64 23 77 130 72 15 0 0 0 0 [201] 0 0 0 0 0 0 0 1 0 69 207 223 218 216 216 188 154 191 210 204 209 222 228 225 0 98 233 198 210 222 229 229 234 [541] 249 220 194 215 217 241 65 73 106 117 168 219 221 215 217 223 223 224 229 29 [561] 75 204 212 204 193 205 211 225 216 185 197 206 198 213 240 195 227 245 239 223 [581] 218 212 209 222 220 221 230 67 48 203 183 194 213 197 185 190 194 192 202 214 [601] 219 221 220 236 225 216 199 206 186 181 177 172 181 205 206 115 0 122 219 193 [621] 179 171 183 196 204 210 213 207 211 210 200 196 194 191 195 191 198 192 176 156 [641] 167 177 210 92 0 0 74 189 212 191 175 172 175 191 179 182 182 181 176 166 168 99 58 0 0 [701] 0 0 0 0 0 0 0 40 61 44 72 41 35 0 0 0 0 0 0 0 [721] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [741] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [761] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 [781] 0 0 0 0 |
请注意图像数据的第一条记录包含 784 个整数值。每个数字对应 28×28 图像中的一个像素。我们将使用这些值作为机器学习模型的输入,处理图像识别。
审查类别
我们可以通过 trainData$y[1]
值来查看该图像的类别。如下所示。
1 2 | trainData$y[1] 9 |
第一张图片的类别是 9
,对应的是 踝靴
。
图像可视化
我们还有一个可视化图像数据的辅助方法,名为 show_digit
。我们可以用它来查看每张图像,了解数据的实际显示效果。
1 2 | show_digit(trainData$x[1,]) |
运行上述代码后,图像输出如下所示。
显示 fashion-mnist 数据集中的第一张图片
设计图像识别模型
让我们来看看处理时尚数据集的模型是什么样的。既然要进行图像识别,我们就需要为机器学习模型提供像素输入。 我们还需要从模型中获取 10 个不同的类别作为输出,并确定每张图像的分类类型。让我们从输入开始。
由于输入是一幅 28×28 像素的灰度图像,我们最终会有 28 * 28 = 784 个输入。我们还将有 10 个输出,每个类别或等级一个。我们可以用下面的神经网络模型来直观地展示这一设计。
用于识别 28×28 像素图像的神经网络机器学习模型
在上述设计中,我们提供了 784 个输入,(28×28)图像中的每个像素都有一个输入。我们将收到 10 个不同的输出,每个输出代表时尚对象可被归类为的每个独特类别。 隐藏层可以由任意数量的神经元和层组成,这取决于你希望你的神经网络有多深或多浅。 要实现更深的抽象和更高的处理结果,例如深度学习,一般需要更多的隐藏层和神经元。就我们的目的而言,一个相对浅层的神经网络就足够了。
当然,神经网络只是一种可用于图像识别和分类的机器学习模型。实际上,我们将使用几种不同的机器学习算法来建立模型,并比较它们的准确性结果。 其中包括逻辑回归、支持向量机和提升树。
让我们来看看上述简单图像识别模型所能达到的一些精确度结果。
准备标签
首先,让我们将每张图像的 Y
标签转换成一个因子。这样,它们就可以在所有图像数据中成为唯一的类别。我们可以使用下面显示的代码来完成这项工作。
1 2 3 | trainData$y <- as.factor(trainData$y) testData$y <- as.factor(testData$y) |
此外,让我们为因子分配可读标签,这样我们就能识别每张图像的类别,而无需查找每个预测类别的数值。
1 2 3 4 5 6 7 8 | labels <- c(‘T-shirt/top’, ‘Trouser’, ‘Pullover’, ‘Dress’, ‘Coat’, ‘Sandal’, ‘Shirt’, ‘Sneaker’, ‘Bag’, ‘Ankle boot’) levels(trainData$y) <- labels levels(testData$y) <- labels dataTrain <- data.frame(x = trainData$x[1:10000,], y = trainData$y[1:10000]) dataTest <- data.frame(x = testData$x, y = testData$y) |
正如您在上述代码中看到的,我们将训练数据限制在前 10,000 张图像。通过这种方式,我们可以加快训练机器学习模型的处理时间。根据您的电脑速度,您可能希望在全部数据集上进行训练。 请记住,由于我们拥有训练集和测试集的标签,因此可以将两者结合起来,在整个数据集(70,000 张图像)上进行训练,从而进一步提高准确率。
数据设置完成后,我们就可以开始训练了。但首先,让我们看看在不使用任何机器学习的情况下,数据的基准准确率是多少。
确定基准精度
在训练机器学习模型之前,重要的是查看在没有机器学习的情况下对图像进行分类的基准准确率。 这样,我们就能更好地了解人工智能机器学习模型是否真的在学习,从而改进非机器学习算法方法。
产生基准准确度结果的常用方法是简单地猜测。在这种情况下,我们可以简单地选择出现频率最高的时尚对象类别,并假设每张图像都是这一类别。 毕竟,如果我们预测每张图片最常出现的类别,那么我们对每张图片预测正确的几率就会比猜测其他类别的几率稍高一些。
我们有多少种图像?
既然我们要找出出现频率最高的图像类别,为了计算出一个简单的基线结果,让我们看看我们的数据集中到底有多少种不同的图像类别。
1 | table(dataTrain$y) |
通过上述代码,我们可以得到下表中的机器学习图像识别类别(记住,我们只查看了前 10,000 张图像!):
1 2 3 4 | T-shirt/top Trouser Pullover Dress Coat Sandal Shirt 942 1027 1016 1019 974 989 1021 Sneaker Bag Ankle boot 1022 990 1000 |
从上表可以看出, "裤子 "
是出现频率最高的图像类别。因此,如果我们对数据集中的每张图像都预测为 "裤子"
,那么在完全猜测一个类别时,我们就能获得尽可能高的准确率。这就是我们的基准准确率。
基准精度
现在,只要用类别列表中的最大数量除以训练集中的图像数量,就能计算出这个 “猜测 “指标的基准准确率。
1 | max(table(dataTrain$y)) / nrow(dataTrain) |
我们最终得到的准确率为 0.1027
或 10.27%
。这似乎并不太理想,尤其是考虑到我们有 10 个不同类别的图像。在 10 个类别中,猜测任何一个类别(在完全平衡的数据集上)的准确率约为 10%。 当然,由于我们的数据集在不同类别之间确实(大部分)是平衡的,这也解释了为什么我们的基准准确率约为 10%。由于数据集中 “裤子 “对象的图片数量比其他类型的图片多,所以我们的准确率略有提高。
回想一下,我们只查看了数据集中前 10,000 张图片的类别。如果你实际查看训练集中所有 60,000 张图片的类别,它们在所有类别中实际上是完全平衡的! 在整个训练图像数据集中,每个类别有 6000 张图像。测试数据集也是如此,每个类别有 1,000 张图像。
现在我们有了 10.27% 的基准猜测准确率,让我们看看机器学习图像识别是否能做得更好。
成果
对于我们应用到数据中的每个机器学习模型,我们都将使用以下代码来测量混淆矩阵中的准确性结果和总体准确性。回想一下,为了更快地获得结果,我们只在 10,000 张图片的数据子集上进行训练。
1 | trainctrl <- trainControl(verboseIter = TRUE, number=5, repeats=1, method=’repeatedcv’) fit <- train(y ~ ., data=dataTrain, method = ‘gbm’, trControl = trainctrl) confusionMatrix(predict(fit, dataTrain), dataTrain$y) confusionMatrix(predict(fit, dataTest), dataTest$y) length(which(predict(fit, dataTrain) == dataTrain$y)) / nrow(dataTrain) length(which(predict(fit, dataTest) == dataTest$y)) / nrow(dataTest) |
混淆矩阵如下
1 2 3 4 5 6 | Confusion Matrix and Statistics Reference Prediction T-shirt/top Trouser Pullover Dress Coat Sandal Shirt Sneaker Bag Ankle boot T-shirt/top 815 2 7 25 3 0 115 0 0 0 Trouser 1 1012 0 5 0 1 0 0 3 0 Pullover 8 1 823 9 72 0 81 0 2 0 Dress 39 11 10 941 27 0 34 0 6 0 Coat 6 1 96 23 817 0 75 0 7 0 Sandal 0 0 1 0 0 969 1 6 0 5 Shirt 63 0 75 16 54 0 703 0 10 0 Sneaker 0 0 0 0 0 12 0 989 2 18 Bag 10 0 4 0 1 2 12 1 958 0 Ankle boot 0 0 0 0 0 5 0 26 2 977 |
在混淆矩阵中,最佳准确度将显示为一条从预测类别的左上方到右下方的大值对角线。对于每个参考类别,您希望看到同一类别预测的最高值。
上述混淆矩阵(基于梯度提升机模型的结果)的准确率为 90%。
让我们来看看我们的成果。
逻辑回归
81.3% / 74.8%
使用 提升逻辑回归 的机器学习模型,我们可以用以下代码计算出准确率。
1 | fit <- train(y ~ ., data=dataTrain, method = ‘LogitBoost’, trControl = trainctrl) |
增强树(GBM / XGBoost)
90.1% / 85.3%
通过 提升树 ,我们可以得到以下结果。
1 | fit <- train(y ~ ., data=dataTrain, method = ‘gbm’, trControl = trainctrl) |
神经网络/多项式回归
83.5% / 78.3%
通过 多项式回归 ,我们可以得到以下结果。
1 | fit <- train(y ~ ., data=dataTrain, method = ‘multinom’, trControl = trainctrl, MaxNWts = 10000) |
支持向量机 (SVM)
91.2% / 87.2%
通过 支持向量机 ,我们可以得到以下结果。
1 | fit <- train(y ~ ., data=dataTrain, method = ‘svmRadial’, trControl = trainctrl) |
我们学到什么了吗?
从上面显示的机器学习图像识别准确率中,我们可以看到机器学习模型取得的结果肯定比我们的基线 “猜测 “模型要好得多。 我们在 10,000 张图片上训练出的最佳模型(SVM)在训练集上的准确率为 91%,在测试集上的准确率为 87%。 如果将其与我们的基准准确率(”猜测 “模型)进行比较,我们的准确率仅为 10%,而 “猜测 “模型只是预测了每张图片中出现频率最高的类别(裤子)。
与基线模型相比,我们的机器学习图像识别预测模型的准确率大幅提高,证明我们的模型确实比简单的猜测更好。
事实上,让我们来看看我们的模型在处理网络上的实时时尚图片时效果如何。
在真实时尚图片上测试模特
要在真实图片上测试我们的模型,我们可以对想要测试的时尚类别进行在线 图片搜索 。例如,让我们试试 连衣裙 。
调整图像大小以进行分类
由于我们的机器学习模型是在大小为 28×28 像素的图像上训练出来的,因此在使用人工智能机器学习图像识别模型处理图像之前,我们只需调整图像的大小。为此,我们只需下载图像,然后在任何 绘画程序 中进行编辑,将其调整到正确的尺寸。请记住,将图像调整为 28×28 像素的固定分辨率很可能会扭曲长宽比和图像质量,从而导致分类预测的可靠性降低。 不过,它还是让我们了解了图像识别机器学习算法的工作原理。
除了将大小调整为 28×28 像素外,我们还需要将背景涂成黑色。预处理完成后,我们就可以将图像加载到模型中了。图像显示如下。
用于机器学习图像识别的 28×28 像素连衣裙图像特写。
由于上图是 28×28 像素图像的特写(虽然人工智能对其进行分类没有问题,但看到的图像还是相当小的!),下面是真实分辨率的实际图像。 您可以了解到这些图像有多小,但人工智能仍能成功进行分类!
用于机器学习图像识别的 28×28 像素连衣裙图像的实际视图。
这张图很小,但我们的机器学习模型经过训练可以处理它。让我们通过图像识别模型运行该图像,看看人工智能将该图像识别为哪个类别。
加载图像并进行分类
我们可以用以下代码加载图像并对其进行分类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | runTest <- function(filename, model) { png <- loadImage(filename) show_digit(png$bytes) pngData <- data.frame(x = matrix(png$bytes, 1, 784)) predict(model, pngData) } |
上述辅助方法只是简单地加载 png 图像。然后显示图像,以便我们看到图像的外观。然后,它将图片中的字节转换为我们的模型所需的格式(灰度,共有 784 个整数值)。最后,它调用 预测
方法将图片分类为特定类型的时尚物品。
测试图像的最终结果
最后,我们可以运行辅助方法来加载和分类图像。让我们看看它是怎么做的。
1 | runTest(‘data/test/dress-28×28.png’, fit) |
上述测试的输出结果如下。
1 | [1] Dress |
成功!
在真实的测试数据上检查机器学习模型是一种方便、人性化的方法,可以展示机器学习模型的准确性。当然,实际的统计精确度要高得多,但直观演示还是很有帮助的。 可视化测试和统计准确度测量都是显示我们的机器学习模型实际学习准确度的方法。
我们还可以通过绘制 学习曲线 来证明我们的机器学习模型能够预测出更好的结果,并且确实在 学习 。
学习曲线
学习曲线是 观察准确性的 一种方法
详细考虑一下这个想法。如果只在一张图片上训练一个模型,它应该很容易就能学会正确分类。毕竟,这只是 1 张图像和 1 个类别。这很容易。这样一来,1 张图片的准确率就能达到 100%。 现在,在不同类别的测试图像上运行相同的模型。它很有可能会预测这张测试图片与单张训练图片的类别相同。毕竟,它所见过的唯一图像就是单张训练图像。 因此,它很可能也会预测出测试图像的相同类别。这导致准确率为 0%。在这种情况下,两个准确率相差最大(100% 对 0%)。
接下来,我们考虑用稍大一点的 10 幅图像数据集来实现同样的想法。由于机器学习模型只需学习 10 张不同的图像,因此它仍能获得很高的准确率。现在针对测试集运行该模型。 虽然它的准确率可能比 0% 稍高一些(正如我们在只对 1 幅图像进行训练时所看到的那样),但我们预计它的表现仍然会很差。 这是因为模型现在已经看到了 10 张不同的图像,使其更有可能在测试集中至少正确获得 1 张图像。这可能会表现为 99% 的准确率相对于 1% 或类似的准确率。
一般来说,机器学习模型接触的训练数据越多,训练准确率就越低,测试准确率就越高。模型在更多的训练数据中学习会变得更加困难。 不过,数据范围可以让它更好地归纳出以前未见过的新图像。到了一定程度,两种准确度开始趋同(或至少接近趋同)。
通过这种方式,学习曲线可以告诉我们,我们的模型是否真的在从数据中学习,并在交叉验证或测试集上通过越来越大的数据集获得更好的准确度。 我们还可以了解更多的数据是否会使我们的模型变得更好!
我们的时尚学习曲线
为了证明我们的机器学习图像识别模型确实在学习,我们可以绘制数据在越来越大的训练集中的学习曲线。我们将绘制训练集准确率和测试集准确率的对比图。 我们可以使用从 1,000 张图像到 60,000 张图像的训练集。这样会产生如下学习曲线。
用于时装分类的图像识别学习曲线
正如上文所述,我们可以清楚地看到,学习曲线由两条向下和向右的弧线组成,似乎最终趋于一致。训练集的准确率几乎达到 100%,而测试集的准确率则低得多,大约只有 60% 或更低。 随着更多的训练数据被输入到机器学习模型中,我们的训练准确率迅速下降到 80% 左右,而测试集的准确率则逐渐提高到 75% 或更高。
如果我们在训练集和测试集之间绘制一条趋势线,就会更清楚地看到,在大约 55000 张训练图像之后,准确率水平开始趋近于 75% 左右。
收敛时带有平滑趋势线的时装分类图像识别学习曲线
从学习曲线图中可以看出,在使用 60,000 张图像进行训练后,训练集和测试集的准确率似乎都趋近于 78% 左右。 如果我们输入更多的图像数据来训练模型,我们的模型似乎不会比这更好。 这是使用学习曲线的主要好处,因为它可以告诉我们是否应该花时间寻找更多的训练数据,或者花时间微调我们的模型和特征列表。
建立学习曲线
既然我们知道了学习曲线的作用,那就让我们来看看如何 构建 学习曲线吧
要从我们的图像识别时尚数据集中建立学习曲线,我们只需在一个循环中迭代,每次都在更大的数据集上训练我们的模型。我们可以用下面的代码来实现这一目标。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | library(reshape2) results <- data.frame() for (i in 1:30) { partialSet <- dataTrain[1:1000 * i,] fit <- train(y ~ ., data=partialSet, method = ‘LogitBoost’, trControl = trainctrl) correct1 <- length(which(predict(fit, partialSet) == partialSet$y)) / nrow(partialSet) correct2 <- length(which(predict(fit, dataTest) == dataTest$y)) / nrow(dataTest) results <- rbind(results, c(correct1, correct2)) names(results) <- c(‘Train’, ‘CV’) r <- melt(results) r <- cbind(r, seq(from = 1000, to = nrow(results) * 1000, by = 1000)) names(r) <- c(‘Set’, ‘Accuracy’, ‘Count’) print(ggplot(data = r, aes(x = Count, y = Accuracy, colour = Set)) + geom_line() + geom_smooth(method = ‘lm’, se=F)) } |
在上述代码中,请注意我们只是调用了一个 for
学习曲线在分析机器学习模型的性能方面可以发挥难以置信的作用。例如,它们可以显示模型内部的 偏差和差异 ,以及在更大数据集上学习的成功率。因此,在验证人工智能机器学习模型时, 了解 并考虑使用学习曲线非常重要。
下载 @ GitHub
该项目的 源代码 可在 GitHub 上获取。
原文链接: https://www.primaryobjects.com/2017/10/23/image-recognition-for-fashion-with-machine-learning/