通过时差说明在线学习
强化学习的基础
介绍
在我们关于 GradientCrescent 强化学习基础的文章中,我们研究了基于模型和基于样本的强化学习方法。简而言之,前一类的特点是需要了解所有可能的状态转移的完整概率分布,并以马尔可夫决策过程为例。相比之下,基于样本的学习方法允许简单地通过重复观察来确定状态值,消除了对转换动态的需要。在我们的上一篇文章中,我们讨论了蒙特卡罗方法在简单地通过环境采样来确定不同状态和行为的值中的应用。
更一般地,蒙特卡罗方法属于离线学习方法家族,就此而言,仅当达到终端状态时,或者在一集结束时,才允许更新状态的值。虽然这种方法对于许多受控或模拟环境来说似乎足够了,但对于需要快速变化的应用来说,比如在自动驾驶汽车的训练中,这种方法就远远不够了。在这种应用中使用离线学习可能会导致事故,因为更新状态值的延迟会导致不可接受的生命或财产损失。
Different agents playing Breakout via OpenAI’s gym utilizing DQN, an online-learning approach based on TD.
因此,今天使用的大多数强化学习算法都被归类为在线学习。换句话说,状态和动作的值是通过一系列的估计不断更新的。这也被称为时间差异学习,是更高级算法的基础,这些算法用于训练代理处理游戏环境,如在 OpenAI Atari 健身房观察到的环境。
时间差异学习
正如在蒙特卡罗中一样,时间差分学习(TD)是一种基于采样的方法,因此不需要模型知识来估计其价值函数。然而,与蒙特卡罗方法不同,TD 是一种在线方法,依赖于具有增量时间步长的剧集内更新。时间差异学习的核心是状态 *St,*的增量更新函数(“自举”),其特征在于 TD 误差(在底部以红色显示):
Monte Carlo (top) versus Temporal Difference (bottom) update functions.
注意在 TD 更新函数中引入了两个不同的时间步长( t 和 t+1 )。TD 误差包含下一时间步的返回与状态 St+1 的当前估计值之和,并从该和中减去前一状态 St 的值。本质上,我们用在稍后的时间步骤中获得的另一个估计值来更新状态的估计值,在之前对于神经网络观察到的复制梯度下降中。
这在实践中是如何运作的?考虑状态(S)、动作(A)和(奖励)的顺序
由于这种循环行为,我们可以在到达下一个状态时更新前一个状态的值。更正式的定义,
时间差分学习与蒙特卡罗
为了更好地说明在线学习和离线学习的区别,考虑一下预测从办公室回家的路程的例子,这是在阿尔伯塔大学的强化学习课程中引入的。在下面指定的每个位置或州,预测的剩余时间以分钟为单位显示在圆圈内,实际经过的时间显示在每个州之间。
Locations, predictions, and time elapsed on a trip home from the office
让我们首先使用蒙特卡罗方法。我们将等待这一集结束,以获取旅行的实际总耗时,然后通过用返回更新值来更新我们的每个状态。因此,在出发点(离开)的情况下,我们可以累积我们的回报(在这种情况下没有折扣因子),并在剧集结束时更新其值,如下所示:
类似地,我们可以通过使用 38 的实际运行返回来更新下一个状态的估计值 35:
然后,我们依次对每个中间目的地重复这一过程,并实现最终更新,如下所示:
因此,我们的状态值现在更好地反映了我们旅行的实际花费时间。**但是,请注意我们是如何不得不等到旅程结束时才执行更新的。**如果我们的旅行目标是在特定时间在中间目的地取包裹,会怎么样?延迟更新可能会导致明显的延迟。
让我们使用时间差异分析来重复我们的估计。使用我们新的估计和实际经过的时间,我们可以使用时间差分析来更新我们先前的初始估计。从办公室 (t) 出发,估计要 30 分钟才能到家。然而,在五分钟 (t+1) 后到达“退出”状态,我们发现我们落后于计划,因此将时间步长 t 的剩余时间更新为 40 分钟。
继续前行,我们需要 15 分钟到达“出口高速公路”,从那里我们估计还需要 15 分钟才能到家。由于这比我们预期的要快,我们可以用这个增量回报来更新我们之前的估计:
我们可以在整个旅程中重复这个过程。
比较这两种方法,很明显,时间差异分析允许进入集内优化,增加我们的代理的反应能力,以更好地收敛,在最短的时间内找到最佳策略。在自动驾驶应用中,这将允许我们在更早的时间点监控和评估代理的性能,并允许我们更快地做出调整,防止探索中不必要的事故。
这就结束了对时间差异分析的介绍。在下一篇教程中,我们将在此基础上,通过 SARSA 和 Q-learning 介绍状态动作值的 TD,后者是一种众所周知的在线学习方法,用于在各种游戏环境中优化代理策略。
我们希望您喜欢这篇文章,并希望您查看关于gradient crescent covering applied AI的许多其他文章。要了解 GradientCrescent 的最新动态,请考虑关注该出版物。
参考文献
萨顿等人。强化学习
怀特等人。阿尔伯塔大学强化学习基础
席尔瓦等人。阿尔,强化学习,UCL
用 ROC 曲线说明预测模型
数据科学是一个在过去几年中真正成长起来的术语,似乎每个人都想加入进来。最有吸引力的目标之一是利用数据资产的力量来创建能够预测各种结果的机器学习模型。有了一个定义良好的模型,人们可以确定能够预测结果的最具影响力的因素,为战略假设开发有价值的洞察力,甚至可以通过友好的用户界面将模型的逻辑实现到软件应用程序中。
然而,在这种神奇的事情发生之前,我们需要知道这个模型所创造的预测是否是好的!例如,如果我们的电子邮件程序的垃圾邮件分类器只能检测到 50%的不受欢迎的电子邮件或请求,我们都会非常愤怒。在这篇文章中,我将介绍如何使用每个数据科学家都应该熟悉的经典工具来评估预测模型:接收器工作特性(ROC)曲线。
说明性的例子:预测冠心病
我在这篇文章中使用的数据集来自 Bom 等人的“靶向蛋白质组学对疑似冠心病患者冠状动脉斑块形态的预测价值”,公众可以通过 https://data.mendeley.com/datasets/gdfvxvr7f2/1获得。
这项研究检查了两种不同的结果,我在这篇文章中重点关注的是没有冠状动脉疾病(CAD)。作者评估了蛋白质组生物标志物检测有症状患者是否存在 CAD 的预测能力。识别一组能够区分非冠心病患者和需要立即干预的患者的蛋白质将提供更准确和成本有效的非侵入性诊断测试。
这个数据集是测试几个数据科学主题的绝佳来源。对于少量的观察值,很容易处理,但是它也包含了大量的变量,增加了复杂性。一个可行的“金标准”的结果(使用冠状动脉计算机断层血管造影,或 CCTA)是用来测试预测。在这篇文章中,我将主要关注单个模型的 ROC 曲线的构建,但在后面的文章中,我可能会深入到更高级的主题。
预测精度测量概述
在开始创建曲线之前,了解一些评估预测准确性的常用指标很重要。
**正类:**我将把正类定义为我试图检测的结果类。在这种情况下,就是 CAD 的 缺席。虽然我意识到这可能会导致诊断测试领域中术语“阳性”和“阴性”的混淆,但是以这种方式定义阳性类更适用于其他情况。
在这种情况下,处理有症状患者的默认方法是让他们接受进一步的测试和程序。通过充分检测没有冠心病的患者,我们将不再需要不必要的、更具侵入性的手术。
一般准确性:简单来说,有多少受试者被正确分类?
**灵敏度:**正确识别的真阳性的比例。在这种情况下,由诊断工具正确识别的健康患者的比例。这有时被称为“召回”
- SN =真阳性/(真阳性+假阴性)
- 逆(1-灵敏度)= 假阴性率。该工具未检测到的健康患者被错误地识别为患有 CAD。假阴性也被称为第二类错误。
**特异性:**正确识别的真阴性的比例。在这种情况下,诊断工具正确识别的 CAD 患者的比例。
- SP =真阴性/(真阴性+假阳性)
- 逆(1-特异性)= 假阳性率。冠心病患者被错误地认定为无冠心病。假阳性也称为 I 型错误。
**阳性预测值:**工具报告的阳性结果的比例,实际上是阳性的。对于诊断工具报告没有 CAD 的患者组,PPV 是实际上没有该疾病的患者的比例。这有时被称为“精度”
- PPV =真阳性/(真阳性+假阳性)
**阴性预测值:**工具报告的阴性比例,实际上是阴性的。对于诊断工具报告存在 CAD 的患者组,NPV 是实际上没有 CAD 的患者的比例。
- NPV =真阴性/(真阴性+假阴性)
如果你在试图整理所有这些指标时头疼,不要担心。很容易混淆这些术语,尤其是在第一次学习它们的时候。我发现在一个 2x2 的表格中可视化事物是最容易的。
Confusion Matrix for a Binary Class ( positive = absence of CAD, negative = CAD)
曲线的剖析
ROC 曲线的好处在于,它是一个易于解释的图形工具,可以应用于您创建的任何预测模型。以下是曲线的基础:
坐标轴:灵敏度和假阳性率
首先,我们需要为情节创造空间。ROC 曲线是通过绘制灵敏度对 1-特异性(或假阳性率)的曲线建立的。
预测概率
现在我们需要一些东西来绘制。回想一下,预测模型会将每个观察值分配到最可能的类别(在这种情况下,没有 CAD 对有 CAD)。模型实际上做的是计算属于特定类别的概率。临界值在 0 和 1 之间选择,如果计算出的概率超过该阈值,则该观察值被分配给该类。
您可能会发现,在大多数包中,默认的截止值被设置为 0.5,其逻辑是,在二进制类中,人们会将一个观察值分配给一个最有可能的类。然而,正如我们将会看到的,最好在考虑到敏感性和特异性之间的权衡之后选择临界值。
ROC 曲线是通过绘制所有可能的临界值生成的,临界值是分配给每个观察值的概率。选择不同的临界值将改变预测工具的灵敏度和特异性,因此,可以使用相关的灵敏度和 1-特异性作为坐标,在图的空间中绘制每个截止概率。最靠近左上角的点(SN = 1,FPR = 0)在精度指标之间提供了最大的平衡。
曲线下面积
AUC 是一种类似于二元模型的一致性或 c 统计量的指标。这是正类中的观察比负类中的观察具有更大预测概率的概率。
如果 AUC = 1,这意味着模型有完美的预测。如果 AUC = 0.5,这将意味着模型不能区分类别。它在逻辑回归中的表现也类似于 r-square,因为增加更多的预测因子将增加 AUC。因此,在分析中包括交叉验证或对外部数据的验证非常重要。
AUC 可用于评估不同的预测模型。
参考线
通常画一条参考线是个好主意,在 ROC 图上 AUC = 0.5。这提供了与曲线进行比较的基线视觉。
生成 ROC 曲线
虽然可以使用几个不同的程序来开发预测模型和 ROC 曲线,但是我在 r。
分析这个数据集的完整代码可以在我的 Github 上找到:
靶向蛋白质组学数据集的数据分析预测有症状患者的冠状动脉疾病…
github.com](https://github.com/dslans/predicting-cad)
为了预测结果,我使用 xgboost 创建了一个分类模型,将蛋白质生物标志物作为预测器,并使用一种替代方法来预测数据集中的结果。使用预测的概率,我可以形成我上面展示的 ROC 曲线。
注意:该程序还被设置为使用 k-fold 交叉验证,或者可以被更新为利用外部验证,比如对训练集和测试集进行 70/30 的分割(尽管我会想要更多的数据)。
解读曲线
AUC 为 0.738,实际上刚好低于作者用他们的机器学习方法发现的值(堆叠可能提高了分类器的准确性)。然而,我更关心的是选择一个合适的截止值,并权衡该工具的成本和收益。
该模型的总体准确性还不错,但当假阳性结果出现时,就会发生这种情况,患有 CAD 的患者会被送回家,而不是通过必要的程序?我们一致认为这不是一件好事。
为了安全起见,让诊断工具的假阳性率尽可能小可能是个好主意。在生成混淆矩阵时,很容易选择一个概率截止值来保持较低的假阳性率。这将作为谁被贴上无 CAD 标签的决定标准。
通过选择保持 90%特异性(10%假阳性率)的概率截止值,分类工具能够检测 42%没有 CAD 的患者(灵敏度= 0.42)。在这种情况下,这实际上是一个非常好的结果,因为现状是让所有有症状的患者接受更具侵入性的诊断程序。尽管 42%的敏感度听起来很低,但在这个特定的场景中,这是一个有希望的结果。
在开发用于在有症状的患者中预测 CAD 状态的分类器时,最重要的是在保持低假阳性率的同时实现尽可能高的灵敏度。
ROC 曲线的美妙之处在于,您可以从一张图片中看到所有这些性能指标。将曲线与竞争模型进行比较是选择合适的分类或诊断工具的快速简便的方法。
我只探索了使用 ROC 曲线和二元分类工具,但是它可以很容易地扩展到多类场景。在同一个图形上,可以绘制出对应于每个特定类别的概率的多条曲线。
在撰写本文时,我详细介绍了如何构建 ROC 曲线,目的是增加对预测准确性度量的理解,以及如何评估您可能正在构建的这些数据科学分类工具。
参考
Bom MJ,Levin E,Driessen RS,等。靶向蛋白质组学对疑似冠心病患者冠状动脉斑块形态的预测价值。EBioMedicine。;39:109–117.doi:10.1016/j . ebiom . 2018 . 12 . 033
Python 插图:置信区间
本文使用了中的知识,中心极限定理,还有的概念,弱大数定律和的切比雪夫不等式,你可以通过访问链接来复习这些题目。如果你想继续下去,你可以从这个链接获得代码: Jupyter 笔记本。
在我看来,这个话题是最令人困惑的定理,甚至维基百科的文章也提到了对它的误解,所以我会尽力解释它。
在深入研究之前,请记住,总体的均值(我们要估计的东西)是一个常数,这个数字没有随机性。
置信区间是我们用来估计总体参数值的估计量。间隔将创建一个可能包含这些值的范围。当我们创建区间时,我们使用样本均值。回想一下中心极限定理,如果我们多次采样,样本均值将呈正态分布。
我创建了样本均值分布来演示这个估计量。
# use gamma distribution
shape, scale = 2.0, 2.0 # mean=4, std=2*sqrt(2)
s = np.random.gamma(shape, scale, 1000000)
mu = shape*scale # mean and standard deviation
sigma = scale*np.sqrt(shape)# create sample mean distribution
meansample = []
# sample size
samplesize = 500
for j in range(0,50000):
# sampling 500 sample from population
rc = random.choices(s, k=samplesize)
# collect mean of each sample
meansample.append(sum(rc)/len(rc))plt.figure(figsize=(20,10))
plt.hist(meansample, 200, density=True, color='lightblue')
plt.show()
样本均值分布是正态分布,均值等于总体均值 4。我们可以把这个分布改成标准的正态分布,用 Z 表来计算概率。
我使用 Z 表创建一个覆盖样本平均值 95%的范围,如下图所示。
# set mean and 95% probability
plt.figure(figsize=(20,10))
plt.hist(meansample, 200, density=True, color='lightblue')
plt.plot([mu,mu],[0, 3.2], 'k-', lw=4, color='green')
plt.plot([mu-(1.96*sigma/np.sqrt(samplesize)),mu-(1.96*sigma/np.sqrt(samplesize))],[0, 3.2], 'k-', lw=2, color='navy')
plt.plot([mu+(1.96*sigma/np.sqrt(samplesize)),mu+(1.96*sigma/np.sqrt(samplesize))],[0, 3.2], 'k-', lw=2, color='navy')
plt.show()
The green line is the true mean or population mean. The two blue lines are a 95% border.
如果我们从总体中取样并计算平均值,在 95%的情况下,我们将得到两条蓝线内的平均值。
不幸的是,我们无法采样 50,000 次并创建这个分布。假设我们只能采样 1 次,我们得到的样本平均值为 3.85。我们得到的平均值是黑线。
# suppose that we sample 500 data that has a mean as 3.85
# Xbar mean = 3.85, sigma = sigma/np.sqrt(samplesize)
m, ss = 3.85, sigma/np.sqrt(samplesize) # mean and standard deviation
plt.figure(figsize=(20,10))
plt.hist(meansample, 200, density=True, color='lightblue')
plt.plot([mu,mu],[0, 3.2], 'k-', lw=4, color='green')
plt.plot([mu-(1.96*sigma/np.sqrt(samplesize)),mu-(1.96*sigma/np.sqrt(samplesize))],[0, 3.2], 'k-', lw=2, color='navy')
plt.plot([mu+(1.96*sigma/np.sqrt(samplesize)),mu+(1.96*sigma/np.sqrt(samplesize))],[0, 3.2], 'k-', lw=2, color='navy')
plt.plot([m,m],[0, 3.2], 'k-', lw=2, color='black')
plt.show()
The black line is the mean we got from sampling.
根据我们得到的平均值,我们希望创建一个估计量来估计真实平均值的值,因此我们使用 95%的范围(黄色区域)创建一个区间。稍后我会解释为什么我们使用 95%的范围。
# the interval we create cover the population mean because it is within 95% range from population mean
plt.figure(figsize=(20,10))plt.hist(meansample, 200, density=True, color='lightblue')
plt.plot([mu,mu],[0, 3.2], 'k-', lw=4, color='green')
plt.plot([mu-(1.96*sigma/np.sqrt(samplesize)),mu-(1.96*sigma/np.sqrt(samplesize))],[0, 3.2], 'k-', lw=1, color='navy')
plt.plot([mu+(1.96*sigma/np.sqrt(samplesize)),mu+(1.96*sigma/np.sqrt(samplesize))],[0, 3.2], 'k-', lw=1, color='navy')
plt.plot([m,m],[0, 3.2], 'k-', lw=4, color='black')
plt.plot([m-(1.96*ss),m-(1.96*ss)],[0, 3.2], 'k-', lw=2, color='red')
plt.plot([m+(1.96*ss),m+(1.96*ss)],[0, 3.2], 'k-', lw=2, color='red')
# Create a Rectangle patch
plt.gca().add_patch(plt.Rectangle((m-(1.96*ss), 0),2*(1.96*ss),3.21, fill=True, linewidth=3, fc=(1,1,0,0.3)))
plt.xlim(3.43, 4.67)
plt.show()
The yellow area is an interval of the sample mean.
我们可以看到区间覆盖了真实均值(绿线)。区间(黄色区域)覆盖了真实平均值,因为样本平均值落在总体平均值(绿线)的 95%范围内。
由于样本均值是随机的,因此有 5%的概率均值会超出 95%的范围。当样本平均值超出 95%范围时,就会出现这种情况。
# if the interval is not within 95% range from population mean the interval will not cover the true population mean
plt.figure(figsize=(20,10))
m = 3.72
plt.hist(meansample, 200, density=True, color='lightblue')
plt.plot([mu,mu],[0, 3.2], 'k-', lw=4, color='green')
plt.plot([mu-(1.96*sigma/np.sqrt(samplesize)),mu-(1.96*sigma/np.sqrt(samplesize))],[0, 3.2], 'k-', lw=1, color='navy')
plt.plot([mu+(1.96*sigma/np.sqrt(samplesize)),mu+(1.96*sigma/np.sqrt(samplesize))],[0, 3.2], 'k-', lw=1, color='navy')
plt.plot([m,m],[0, 3.2], 'k-', lw=4, color='black')
plt.plot([m-(1.96*ss),m-(1.96*ss)],[0, 3.2], 'k-', lw=2, color='red')
plt.plot([m+(1.96*ss),m+(1.96*ss)],[0, 3.2], 'k-', lw=2, color='red')
# Create a Rectangle patch
plt.gca().add_patch(plt.Rectangle((m-(1.96*ss), 0),2*(1.96*ss),3.21, fill=True, linewidth=3, fc=(1,1,0,0.3)))
plt.xlim(3.43, 4.67)
plt.show()
If we sample and get the mean that falls outside blue lines, the interval will not cover the true mean.
黄色区域不包含人口平均值。
我们使用 95%的范围,因为它与样本均值的概率相匹配。如果样本均值在概率的边界上,则该范围仍然覆盖真实均值,但如果再远一点,则不会覆盖真实均值。样本均值落在两条蓝线内的概率为 95%,因此两条线外 5%的区间不会覆盖真实均值。
95%这个数字是一个任意的数字,你可以把它设置成任意的数字。例如,我们可以将其设置为 90%的范围。
# use 90% instead of 95%
plt.figure(figsize=(20,10))
m = 3.72
plt.hist(meansample, 200, density=True, color='lightblue')
plt.plot([mu,mu],[0, 3.2], 'k-', lw=4, color='green')
plt.plot([mu-(1.645*sigma/np.sqrt(samplesize)),mu-(1.645*sigma/np.sqrt(samplesize))],[0, 3.2], 'k-', lw=1, color='navy')
plt.plot([mu+(1.645*sigma/np.sqrt(samplesize)),mu+(1.645*sigma/np.sqrt(samplesize))],[0, 3.2], 'k-', lw=1, color='navy')
plt.plot([m,m],[0, 3.2], 'k-', lw=4, color='black')
plt.plot([m-(1.645*ss),m-(1.645*ss)],[0, 3.2], 'k-', lw=2, color='red')
plt.plot([m+(1.645*ss),m+(1.645*ss)],[0, 3.2], 'k-', lw=2, color='red')
# Create a Rectangle patch
plt.gca().add_patch(plt.Rectangle((m-(1.645*ss), 0),2*(1.645*ss),3.21, fill=True, linewidth=3, fc=(1,1,0,0.3)))
plt.xlim(3.43, 4.67)
plt.show()
When we change the confidence from 95% to 90%, the interval becomes narrower.
因此,当我们说 95%的置信度时,意味着我们确信 95%的样本的平均值在真实平均值的 95%范围内(两条蓝线内)。问题是如果我们知道真正的意思,我们就不会这么做了。为了解决这个问题,我们将解释改为“我们确信 95%的抽样,样本将具有一个平均值,该平均值可以创建覆盖真实平均值的区间”。
就像我上面提到的,这里唯一随机的是样本均值,所以我们不能说真均值的概率在区间内,因为总体均值不是随机变量,真均值是一个数字。
理论上,如果我们采样 100 次,95 次,我们将得到一个样本均值,其区间覆盖真实均值,因此我使用 python 模拟 100 次采样,这就是所发生的情况。
# simulate 100 interval with 5,000 sample size
mu = shape*scale # mean
sigma = scale*np.sqrt(shape) # standard deviation
intervallist = []
k = 1.96
# sample size
samplesize = 5000
# start count
c = 0
for i in range(0,100):
# sample 100 sample
rs = random.choices(s, k=samplesize)
# calculate mean
mean = np.mean(rs)
upbound = mean + k*sigma/np.sqrt(samplesize)
lowbound = mean - k*sigma/np.sqrt(samplesize)
# collect difference between sample mean and mu
intervallist.append([lowbound,mean,upbound])
if upbound >= mu and lowbound <= mu:
c += 1
print("number of interval that cover the expected values:", c)
# set figure size.
plt.figure(figsize=(20,10))
# plot box plots of each sample mean.
plt.boxplot(intervallist)
plt.plot([1, 100],[mu,mu], 'k-', lw=2, color='red')
# show plot.
plt.show()
The red line is a true mean, the box plots are intervals.
这个数字可能不是精确的 95,因为它毕竟是一个概率。
最后一点,样本大小对这个话题有巨大的影响。如果样本量增加,样本均值的方差会减小。因此,间隔范围也减小。
# simulate 100 interval with 20,000 sample size
intervallist = []
# sample size
samplesize = 20000
# start count
c = 0
for i in range(0,100):
# sample 100 sample
rs = random.choices(s, k=samplesize)
# calculate mean
mean = np.mean(rs)
upbound = mean + k*sigma/np.sqrt(samplesize)
lowbound = mean - k*sigma/np.sqrt(samplesize)
# collect difference between sample mean and mu
intervallist.append([lowbound,mean,upbound])
if upbound >= mu and lowbound <= mu:
c += 1
print("number of interval that cover the expected values:", c)
# set figure size.
plt.figure(figsize=(20,10))
# plot box plots of each sample mean.
plt.boxplot(intervallist)
plt.plot([1, 100],[mu,mu], 'k-', lw=2, color='red')
# show plot.
plt.show()
Notice the y-axis scale is smaller.
如果我们的样本量很大,估计量会更精确。
代码可以在这个链接找到: Jupyter 笔记本。
我是贝塞德,我知道
Source: deeredeemed.wordpress.com
如果你还太年轻,不知道标题出处,我会让你失去理智。它与聚会、摇滚和圣歌有关。实际上,不,我只是想让你玩得开心,所以我想让你看看标题图片。你注意到了什么?
显然,我让你注意标题和图片是有原因的。有了这个标题,你可能不会意识到它有一个“模式”,直到我指出来。对于这幅图,如果你只是匆匆一瞥,你可能只看到了羊。
如果你在我没有指出来的情况下,设法把两者都搞清楚了,你可以停止阅读了。
对于那些不知道的人来说,我(试着)比你们强一点的原因是因为我想谈谈偏见。不,不是机器学习中的偏见,这是指一个模型不能“学习”任何东西。我将要谈到的偏见更为严重,它与人类而非机器有关。你现在可能已经明白了…我们将要谈论认知偏见。为什么?因为它们通常与数据驱动的决策和数据科学有很大关系。
让我们开门见山吧。下面你会发现一些我们人类的认知偏见。本帖,我只说一个;它对我们的生活有着重大的影响,但也许还没有被人们谈论的那么多。这被称为基础率谬误。
Source & interactive version: Wikipedia
基础利率谬误
想想你最后一次参与项目的时候。它是否比您最初预期的花费了更多的时间?对我来说,在团队工作时,这种情况总是会发生。你还有一周就要截稿了;由于团队花了 4 天时间完成上一个项目,你们都认为这次最多花 4 天时间就能完成。突然到了晚上 11:50,离截止日期还有 5 分钟,在吃了四天街角小店的烤肉串后,你开始争先恐后地完成所有事情。
每当这种情况发生时,我也会责怪我的队友或老板,而不是我自己(顺便说一下,这实际上是一种偏见,自私的偏见),但事实上,我没有忽略基本率,因为作为一个人,我不太擅长将信息与概率分开。为了向你解释什么是基础利率谬误,我将遵从这方面的专家:
两位最著名的行为科学家阿莫斯·特拉弗斯基和丹尼尔·卡内曼进行了一项实验,他们向参与者描述了从 70 名律师和 30 名工程师中随机选出的 5 个人。参与者需要预测这 5 个人是律师还是工程师。Kahneman & Traversky 发现,参与者的预测完全忽略了这个群体的特征(这个群体由 30%的工程师或 70%的律师组成,这是 5 种描述中的每一种落入每一组的各自概率),最终的描述是从这个群体中得出的。相反,参与者似乎将他们对每个人职业的预测建立在描述与典型的律师或工程师相似的程度上。显然,参与者有偏见。如果不是,从 5 个样本中,他们最好说 3 个是律师,2 个是工程师(每个人的期望值)。
这种偏见对你和你未来的小组项目意味着什么?不要考虑以前同一个团队完成一个项目花了多长时间,开始收集在你之前其他团队完成相同或相似项目花了多长时间的数据!这样,你就不会忽略基础利率。
快速提示:显然,有相当大的一个专门的机器学习研究人员小组试图建立可以仅基于相似性进行预测的算法。佩德罗·多明戈斯(Pedro Domingos)在他的书《算法大师》(The Master Algorithm)中,将这些人描述为类比者。
对数据科学的影响
回到正题,基础率谬误不仅仅局限于团体项目。它对决策制定有深远的影响,并与数据科学相关联。通过忽略基础利率,我们可能会基于某人的长相(光环效应,或者为什么骗子会成功)而不是他们的实际支付能力,认为他们不支付信用卡账单的概率更高。另一个例子是医生误诊病人,因为他们没有考虑疾病发生的基本比率,本质上是给非常罕见的疾病附加了更高的概率。
现在戴上你的数据科学家帽子,想想机器学习是如何防止这种偏见干扰决策的?让我给你一个提示:
Thomas Bayes, Source: Wikipedia
事实证明,贝叶斯是没有偏见的。
由托马斯·贝叶斯在 18 世纪首次提出的贝叶斯定理,通过在计算后验概率时结合先验概率来考虑基础率。我不会在这篇文章中花时间解释它是如何工作的,因为佩德罗·多明戈斯做得更好,所以我强烈推荐他的书给那些更渴望了解居住在“ML 土地”上的部落的人。
我要说的是,贝叶斯定理是当今机器学习中最重要(也是最流行)的算法之一,它的应用见于一种叫做朴素贝叶斯的机器学习算法。
那又怎样?
你可能想知道我想证明什么。有些文章将数据科学和/或机器学习描述为会带来大量失业、过滤泡沫等等的东西。对这个行业的看法是,计算机科学家被付钱编写成千上万行代码,因为自动化比付钱给信贷员更便宜,或者更糟的是,公司想要发布假新闻来影响人们的决定。
我在这里的目的是给你一个不同的视角。事实上,许多数据科学都是基于被开发(或重新发现)以摆脱所有人类都有的认知偏见的想法。这包括信贷员和数据科学家。随着偏见的减少,我们以前做错的决定正在变得正确。例如,我们不再有仅仅因为信贷员不喜欢他们的穿着而不给他们贷款的问题(但没有意识到这影响了他/她的决定)。
我并不是说这都是阳光和彩虹,因为有许多关于数据科学的伦理困境正在进行辩论。无可争议的是,由于数据科学,我们在决策中的人类偏见更少了,这改善了全人类的生活。
你同意吗?
基于卷积神经网络的图像美学量化
基于 MobileNetV1 的卷积神经网络(CNN)训练项目报告,仅使用 14,000 幅图像进行迁移学习
雅各布·欧文斯在 Unsplash 上的照片
一.定义
项目概述
“一幅画胜过千言万语”强调了图像在现代世界中的重要性。例如,图像的质量会影响我们在不同领域的决策。尤其是在电子商务领域,我们不能碰的东西是必不可少的。因此,他们对我们的产品购买决策有着重大影响。
订哪个房间?
和哪个家伙约会?
点什么菜?
这个项目的目标是创建一个可以量化图像美学的模型。
问题陈述
图像质量的量化是计算机视觉中的一个老问题。有客观和主观的方法来评估图像质量。使用客观方法,不同的算法量化图像中的失真和退化。主观方法基于人的感知。这些方法往往互不相关。客观方法涉及传统的基于规则的编程,主观方法不能以这种方式解决。
这个项目的目标是开发一种主观的图像质量评估方法。如前所述,这个问题不能用经典编程来解决。然而,有监督的机器学习似乎是解决该问题的完美候选,因为这种方法从示例中学习,并且是一种量化不可言喻的方式。具有图像质量注释的数据集是从样本学习的要求。
在机器学习生态系统中,卷积神经网络(CNN)是一类神经网络,已被证明在图像识别和分类等领域非常有效。它们受到生物过程的启发,因为神经元之间的连接模式类似于人类视觉皮层的组织。
主观质量模型是用卷积神经网络实现的,因为它似乎非常适合解决这个问题。
这些步骤是必要的:
- 查找带有高质量注释的图像数据集
- 对数据集进行探索性数据分析(EDA ),以评估问题空间的特征和适用性
- 数据集的清理和预处理
- CNN 架构设计
- CNN 的培训
- 对照基准测试模型
- 结果分析
步骤 4 有几次迭代。-7.
韵律学
项目中预测了用户评分的分布。从那里你可以预测一个定量的平均评级,也可以预测一个定性的评级桶。为了捕捉这两个指标,使用了。
推土机距离(EMD)
推土机距离(EMD) 是一种评价某个特征空间中两个多维分布之间不相似性的方法,其中给出了单个特征之间的距离度量,我们称之为地面距离。EMD 将这个距离从单个特征“提升”到完全分布。假设表现良好的 CNN 应该预测类别分布,使得更接近地面真实类别的类别应该比更远的类别具有更高的预测概率。对于图像质量评级,分数 4、5 和 6 比 1、5 和 10 更相关,即目标是如果实际分数是 10,那么当实际分数是 5 时,惩罚预测 4。EMD 定义为将一个分布(直方图)的质量转移到另一个分布的最小成本。(侯、于和萨马拉斯,2016 年)(鲁布纳、托马西和吉巴斯,2000 年)和米兰法尔,2018 年)
准确(性)
为了比较定性结果,使用了精度。准确度是正确预测的比率。在这种情况下,在“官方”测试集上使用阈值 5 的地面实况和预测平均值,因为这是 AVA 数据集的标准做法。
数据探索
由(Murray、Marchesotti 和 Perronnin 2012a),(Murray、Marchesotti 和 Perronnin 2012b)介绍的 AVA(美学视觉分析)图像数据集是各种图像美学的参考数据集。该数据集包含 255508 幅图像,以及各种美学、语义和摄影风格的注释。这些图片是从 www.dpchallenge.com 的收集的。
样本行
样本图像
最佳评价图片
评价最差的图片
评级数量的描述性统计
评分均值的描述性统计
探索性可视化
评级数量的分布
每张图片的评分数量:大多数由超过 100 名评分者进行评分
这些图片的评分从 78 到 549 不等,平均分为 210 分,从 1 到 10 分不等。
可以看出,所有的图像都被大量的评价者进行了评价。这是非常重要的,因为通过图像的美学来评价图像是非常主观的。要消除异常值评级,需要大量的评级人。
平均评级的分布
评分均值分布
从分布和描述性统计可以看出,50%的图像具有在 4.9 和 5.9 之间的评级均值,并且大约 85%的图像在 3.9 和 6.8 之间。从箱线图中可以看出,高于 7.2 和低于 3.5 的评级均值是异常值,因为这些值很少。
这是有问题的,因为模型性能不适用于质量优秀和糟糕的图像。
算法和技术
卷积神经网络(CNN)
卷积神经网络(CNN)将用于解决图像美学评估的问题。它们是受生物过程启发的深度神经网络,最常用于分析视觉图像。
CNN 由一个输入层、一个输出层和几个隐藏层组成。隐藏层通常是卷积层,后面是汇集层。
用于图像分类的典型 CNN 的结构。网络的每个卷积层都有多个过滤内核,用于提取特征。二次抽样或汇集层用于减少信息。(来源维基百科)
卷积层
卷积层的目的是从输入图像中提取特征。它们通过使用输入数据的小方块学习图像特征来保持像素之间的空间关系。
卷积运算提取特征
汇集层
卷积网络可以包括汇集层。这些层将一层的神经元簇的输出组合成下一层的单个神经元。这样做的原因如下:
- 内存的减少和执行速度的提高
- 过度拟合的减少
MaxPooling 层,提取一个区域中的最大值来减少信息。(来源维基百科)
全连接层
经过多层卷积层和汇集层,一个全连接层完成了网络。全连接层是负责分类任务的传统多层感知器。
迁移学习
迁移学习是计算机视觉中一种流行的方法,因为它允许我们以省时的方式建立精确的模型(Rawat 和 Wang 2017)。使用迁移学习,你不是从零开始学习过程,而是从解决一个不同的问题时已经学到的模式开始。这样你可以利用以前的经验,避免从头开始。
在计算机视觉中,迁移学习通常通过使用预先训练的模型来表达。预训练模型是在大型基准数据集上训练的模型,用于解决与我们想要解决的问题类似的问题。因此,由于训练这种模型的计算成本,通常的做法是从公开的文献(例如,VGG、Inception、MobileNet)中导入和使用模型。
迁移学习
一些最先进的图像分类应用程序基于迁移学习解决方案(何等人,2016 年),(Szegedy 等人,2016 年)谷歌在其(神经图像评估)论文中报告了基于迁移学习的模型的最高准确性(塔莱比和米兰法尔,2018 年)
该项目的目标是将 MobileNet 架构与 ImageNet 权重结合使用,并将 MobileNet 中的最后一个密集层替换为输出到 10 个类别(得分 1 至 10)的密集层,这些类别与(塔莱比和米兰法尔 2018 年)建议的评级分布一起形成
基准
在不同的论文中报道了不同模型在 AVA 数据集上的精度。这些精确度被用来作为在这个项目中创建的模型的基准。这些基准基于“官方的”AVA 测试集。目标是达到至少 68%的准确度,这高于图像美学相关论文的下限。
三。方法学
数据预处理
数据预处理可以分为两部分:第一部分是在探索性数据分析过程中完成的。在该步骤中,执行了以下检查和清洁:
- 移除图像:
- 一些图像必须从元数据中删除,因为它们不存在。
- 用脚本识别了几个损坏的图像。损坏的图像已从元数据中删除。
2.设计了技术图像属性来检查图像异常
几个技术图像属性(文件大小、分辨率、长宽比)被设计并检查异常。没有异常图像可以在这里确定这些属性。
第二个预处理步骤在训练期间执行:
- 将数据拆分为训练集和验证集
训练集的 10%的图像用于验证。
2.执行了基础模型特定预处理
Keras 提供的每个基础模型都提供了一个预处理函数,并为该模型提供了特定的预处理步骤。这个预处理步骤被应用于图像生成器,该图像生成器加载用于训练和模型评估的图像。
3.分布的标准化
评级分布是标准化的,因为每张图片由不同数量的人进行评级。
4.图像大小调整和随机裁剪
训练图像被重新缩放到 256×256 像素,然后,提取 224×224 像素的随机执行的裁剪。据报道,这可以减少过度拟合问题。(塔莱比和米兰法尔 2018)
5.数据的欠采样
对于更早的训练期,通过在 10 个分级箱中切割数据并取每个箱的前 n 个样本来减少图像的数量。这样做有两个原因:由于计算能力有限,使用的图像较少,这减少了训练模型的时间。另一个原因是数据不平衡。只有很少的几张图片有很低和很高的评分。预计欠采样会降低最常见等级周围的图像过拟合的影响。
履行
目标是创建一个清晰的训练脚本,可以从外部参数化,用于触发不同的训练课程。为了减少这个培训脚本的代码行,它用一个管道脚本来编排培训的构建块。
- 所有需要的库都被确定并放入 requirements.txt 中
- 实现了用于下载 AVA 图像和元数据的内部库。
- 使用用于训练的构建块创建了训练脚本(加载数据、准备数据、训练、评估)
- 培训脚本的构建块被移动到管道脚本中。这些脚本保存了不同的人工制品:模型架构、模型权重、训练历史、训练时间、训练可视化
- 创建了一个模型类,它封装了基础模型和顶层模型,并提供了帮助器函数来动态更改优化器和冻结层
- 创建了 EMD 损失函数
- 创建图像生成器是为了加载图像并执行图像的预处理
- 实现了几个用于模型评估的辅助函数
实际训练分两步进行:
- 基本模型的权重被冻结,只有顶部模型以较高的学习率被训练
- 基本模型权重被解冻,整个网络以较低的学习速率进行训练
CNN 的模型设计
如前所述,该模型由两部分组成。除了移除的第一层之外,基本模型保持不变。用 ImageNet 权重初始化模型。ImageNet 项目是一个广泛的视觉数据库,设计用于视觉对象识别软件研究。由于影像与 AVA 数据集中的影像相似,因此使用该数据集的权重。对于基本模型,使用 MobileNet 架构,因为该网络比其他网络小,适合计算能力不足的基于移动和嵌入式的视觉应用。(霍华德等人 2017 年)
顶层模型由两层组成。第一层是一个下降层,以减少过度拟合,随后是一个密集层,输出大小为 10,激活 softmax 以预测评级分布。具有不同学习速率和学习速率衰减的 Adam 优化器用于训练。
顶层模型的设计:避免过拟合的脱落层,10 个输出类的密集层
精炼
模型优化使用了几个参数:
- 密集层和所有层的学习率
- 密集层和所有层的学习率衰减
- 密集层和所有层的历元数
- 用于训练的每个分级箱的图像数量
- 顶层模型中漏失层的漏失率
训练是迭代完成的:首先,用非常少的样本和上述参数的默认值训练模型。然后用更多的样本对模型进行训练,对参数进行微调。在模型被训练之后,为测试集计算损失值和准确度。然后将准确度与论文中的准确度分数进行比较(参见“基准”一节),直到达到足够的模型准确度。
训练过程由训练和验证集的损失图来监督,以检查一切是否正常,并优化学习过程。
训练历史图用于寻找两个学习阶段的最佳时期数。在第 1 阶段,验证损失在第 5 时段(图中为 4)变平,在第 2 阶段,val 损失在第 8 时段(图中为 12)变平
四。结果
模型评估和验证
在不同的模型中,选择了模型 8,因为它的 EMD 损失值最低,并且在测试集的所有模型中,它的精度最高。结果是可信的,因为测试集是 AVA 的“官方”测试集,模型在训练或验证期间从未“看到”这些图像。一个有趣的事实是,这个模型的表现略好于模型 9,模型 9 是用两倍数量的训练图像训练的。
最佳模型基于 MobileNet 架构,并使用以下参数。所有这些参数似乎都是合理的:
从下图中可以看出,最佳模型的实际平均评级和预测平均评级的分布非常相似。该模型适用于 3.5 到 7.5 之间的平均评分。低于或高于这些界限的评级没有被模型很好地覆盖。这是因为事实上,没有多少图像具有很高和很低的评级。因此,由于缺乏实例,该模型无法正确评估这些极端异常值。
大图:测试集上有蒂平均评分和真实评分的分布。小数字:分布在测试集的下端和上端。
正当理由;辩解
与基准测试相比,该模型在 AVA 的参考测试集上显示了中等的准确性,该测试集在论文的所有模型中使用。
结果相当令人印象深刻,因为该模型仅用 13,914 张图像进行了训练。论文中的模型使用完整的训练集(250,000 幅图像)进行训练。
动词 (verb 的缩写)结论
自由形式可视化
对于最后的快速和肮脏的测试,从“项目概述”部分的图像与模型评级。影像不是 AVA 数据集的一部分。
左图:4.23 右图:3.91
左图:3.27 右图:4.00
左图像:3.98 右图像:4.67
可以看出,虽然食物图像的质量几乎相同,但是我们作为人类会评价得更好的图像也被模型评价得更好。
反射
本项目所用的流程可概括为以下步骤
- 发现一个相关问题
- 对相关论文进行了研究
- 对该问题的数据集进行了研究和分析,并选择了最合适的数据集
- 数据集已被清理
- 模型基准是从论文中摘录的
- 项目的技术基础设施已经建立
- 模型被训练、微调,并对照基准进行检查,直到找到一个足够好的模型,解决问题
由于缺乏计算能力和数据集非常大,该项目非常具有挑战性。直到最后,我都无法在完整的训练集上训练模型,因为总是会出现内存不足、Keras 和 Tensorflow 特有的问题。我在某个时候卡住了,因为模型表现不佳。在做了一轮额外的研究后,我找到了谷歌的 Nima 论文,这是一篇全新的论文,以至于当我 7 月份开始这个项目时,它还没有发表。论文中的见解是一个突破,特别是对推土机 Loss 的使用和对基本模型的 MobileNet 架构的使用。我感到非常自豪的是,我可以在相关论文的范围内获得准确性,并掌握了一个目前非常热门的话题,主要是因为我在论文中使用的图像比研究人员少。
改进
令人兴奋的是,我确实用欠采样策略达到了一定的精度,这一半是出于需要。即使在对数据进行欠采样之后,评级的分布也是不平衡的。
更好地执行的策略是对未被充分表示的评级图像进行图像增强。这并不容易,因为不是每种图像增强都可以使用,例如,使图像变暗可能会影响图像的美观。另一个有趣的方法是用 GANs(生成-对抗-网络)生成具有非常高和非常低评级的图像。
该项目的另一个改进是用 Docker 和 Docker NVIDIA 将整个过程集装箱化。目标是有一个 docker 映像,它自动下载数据,对数据进行预处理,进行训练,并在训练后停止容器。在这个项目中,这是通过 anaconda 环境完成的,在我看来这并不理想。我不得不总是从我的本地环境切换到 AWS 云实例,这浪费了时间,因为环境不一样。Docker 环境也可以通过其他深度学习项目的可重用元素进行优化。
不及物动词参考
何、、、任、。2016."图像识别的深度剩余学习."Ieee 计算机视觉和模式识别会议论文集,770–78。
侯、乐、陈-、迪米特里斯-萨马拉斯。2016.“平方推土机的距离为基础的损失训练深度神经网络.” arXiv 预印本 arXiv:1611.05916 。
Howard,Andrew G,Menglong Zhu,,Dmitry Kalenichenko,,Tobias Weyand,Marco Andreetto 和 Hartwig Adam。2017." Mobilenets:用于移动视觉应用的高效卷积神经网络." arXiv 预印本 arXiv:1704.04861 。
孔、舒、沈晓辉、哲林、拉多米尔·梅克和查尔斯·福尔克斯。"照片美学排名网络与属性和内容适应."在欧洲计算机视觉会议上,662–79 页。斯普林格。
陆,辛,林哲,金海林,杨建超,王。2014." Rapid:使用深度学习评价绘画美学."在第 22 届 Acm 多媒体国际会议论文集,457–66。ACM。
陆、辛哲林、沈晓辉、拉多米尔·梅奇和詹姆斯·Z·王。2015."深层多补丁聚合网络,用于图像风格、美学和质量评估."在Ieee 计算机视觉国际会议论文集,990–98。
默里,奈拉,卢卡·马尔凯索蒂和弗洛伦特·佩罗宁。2012 年 a。" AVA:美学视觉分析的大规模数据库."https://github.com/mtobeiyf/ava_downloader。
— — — .2012 年 b。" AVA:美学视觉分析的大规模数据库."在计算机视觉和模式识别(Cvpr),2012 Ieee 会议上,2408–15。IEEE。
拉瓦特、瓦西姆和王增辉。2017."用于图像分类的深度卷积神经网络:综述."神经计算 29 (9)。麻省理工学院出版社:2352–2449。
Rubner,Yossi,Carlo Tomasi 和 Leonidas J Guibas。2000."推土机的距离作为图像检索的度量."国际计算机视觉杂志 40 卷 2 期。斯普林格:99–121。
施瓦兹、卡塔琳娜、帕特里克·维肖莱克和亨德里克·帕·伦施。2018.“人们会喜欢你的形象吗?学审美空间。”在计算机视觉的应用(Wacv),2018 Ieee 冬季会议于,2048–57。IEEE。
塞格迪,克里斯蒂安,文森特·万霍克,谢尔盖·约菲,乔恩·施伦斯和兹比格涅夫·沃伊纳。2016."重新思考计算机视觉的初始架构."Ieee 计算机视觉和模式识别会议论文集*,2818–26。*
塔莱比、侯赛因和佩曼·米兰法尔。2018.“尼玛:神经影像评估.” IEEE 图像处理汇刊 27 (8)。IEEE:3998–4011。
2019 年 3 月 20 日由延斯·劳弗撰写
随意分享!
原载于 2019 年 3 月 20 日 jenslaufer.com**的 。
使用 Python、Numpy、Opencv 和 Skimage 的图像增强
图像增强是一种策略,使从业者能够显著增加可用于训练模型的图像的多样性,而无需实际收集新图像。对于训练任何机器学习模型,特别是深度学习模型,拥有大型数据集非常重要,可以显著提高模型的性能。当我们在图像上训练深度学习模型时,我们至少需要数万张图像来概括图像的模式。
生成图像数据是昂贵且乏味的工作。没有数据,机器是盲目的。在这种情况下,我们可以通过对现有图像应用不同的变换技术来生成更多的图像。有不同的技术,如旋转、翻转、移动等。这可以帮助我们使图像数据多样化。如果我们至少有 4-5 种变换技术,我们可以生成 10 倍或 100 倍以上的图像。我们也可以使用模糊图像和添加随机噪声的方法来生成更多的图像。
“即使是世界上最强大的模型也不过是没有适当数据的几行代码”。
在这篇文章中,我们将看到如何使用一些变换和方法从 289 幅图像生成 2000 幅图像。我已经使用 NumPy,Skimage 对图像执行了不同的操作,这些操作非常简单,任何人都很容易理解,即使是 python 和 opencv 的新手。我使用了随机图像的数据集,其中一些图像是彩色的,而一些是灰度图像。
彩色图像和灰度图像的区别在于,彩色图像有 R、G 和 B 三个通道,它们相互叠加,而灰度图像只有一个灰色通道。我说的通道是指矩阵。我们都知道图像只是一个像素阵列。例如:高度为 720,宽度为 1200 图像被表示为包含像素值的 720 行和 1200 列的二维阵列。
NumPy 是一个非常强大且易于使用的数字操作库。由于图像只是一组数字,numpy 让我们的工作变得如此简单。
让我们跳到操作。我们将使用翻转、旋转、移动、添加噪声和模糊图像等方法。不要担心编码部分,我已经在接下来的段落中解释了每一个操作。我在文章的最后还提供了源代码的 Github 链接,如果有帮助的话可以参考一下。
**翻转:**要水平翻转图像,我们需要颠倒矩阵列的顺序,这将导致图像水平改变。NumPy 有一个名为 fliplr()的方法,它通过反转列的顺序来返回数组。以同样的方式,有另一个方法 flipud()颠倒行的顺序,导致垂直翻转图像,如下所示。
**旋转:**我们将使用 sklearn 对图像进行旋转、移位添加噪点和模糊处理。Sklearn 提供了 rotate()方法,该方法以角度为参数,返回旋转后的图像。角度的正值逆时针旋转图像,而角度的负值顺时针旋转图像。请参考下图。
45 degree clockwise rotation and vice versa using rotate method
**移动:**我们可以向右、向左、向上、向下或对角移动图像。我们只需要将 dx 和 dy 值传递给函数。在移位操作之后,存在于输入图像中的位置(X,Y)处的对象被移位到新的位置(X,Y):
X = x + dx
Y = y + dy
Image after shifting operation
添加噪声:为了添加噪声,我们将使用 skimage 库中的 random_noise()方法。这种方法将随机噪声添加到图像中,噪声对于正则化的目的是非常有用的。它防止模型过度拟合。
**模糊:**对于模糊图像,我们使用了 opencv 中的 gaussian_blur()方法,该方法以图像和内核大小为参数。内核越大,图像越模糊。
Blurring image
现在,让我们使用所有的方法,并将它们应用于我们的数据集,以生成新的图像。我们将从目录中逐个读取图像,并将它们的名称存储到数组中。我们这样做是为了从目录中随机选择图像。我们将在随机图像上应用随机数的变换,直到生成 2000 个图像。
让我们看看代码部分,并阐明它的每一部分。
我们已经为将在图像上执行的每个操作定义了函数,并将它们的名称存储在字典中,以便我们可以在运行时随机调用它们。
Define functions and create dictionary to store them
让我们看看在对图像执行操作后读取图像并生成增强图像的主要代码。
Heart of the augmentation code.
我们定义了两条路径,一条用于读取图像,另一条用于写入图像。我们已经指定了希望生成的图像数量。
一旦外部 while 循环从图像数组中选择了一个随机图像,我们将读取该图像。使用 randint()方法从 random 库中随机选择应用于读取图像的变换数量。这个外部 while 循环将一直运行,直到 counts (i)达到 2000(要生成的图像数)。
内部 while 循环随机选择方法,并将原始图像传递给该方法,并将转换后的图像存储到 transformed_image 变量中。如果转换次数> 1,那么我们将再次随机选择方法名,并传递转换后的图像,使其再次转换。
在对图像执行随机选择数量的转换后,在将它写入磁盘之前,我们将它传递给 image_as_ubyte()方法。skimage 库中的 image_as_ubyte()函数用于将像素值保持在 0–255 范围内。最后,我们将把我们的图像转换成 RGB 并保存到 Augmented_Image 文件夹,这是我们的目的地。
万岁!!我们已经成功地从 289 张图片中创建了 2000 张图片。我们可以使用 7 种变换方法创建更多的图像。你可以在这里找到数据集,在这里找到源代码。
如果你想阅读更多关于 Python 中数据扩充的内容,你可以点击查看其中一篇有趣的文章。
感谢您的阅读:)
向我奶奶解释图像分类
简明英语中的 CNN 是什么
2019 年,我参加了由 CodeProject 组织的国际活动,我的项目 KerasUI 取得了胜利,这是一个实现用于训练和消费神经网络的 web GUI 的工具。这是一个特别的机会,让我在神经网络和人工智能领域沉寂了几年之后,重新获得这方面的知识。现在两年过去了,我偶尔和朋友和同事聊天,但我仍然觉得社区对这个话题没有正确的认识。除了专家,大多数人根本忽略了什么是图像分类,或者简单地用 TensorFlow 库的一些方法来识别。嗯,这促使我再次写这个话题,只关注理论部分!
在文章的结尾,您会找到阅读获奖文章和下载代码的链接。我试图让事情变得尽可能简单,简单到可以向我奶奶解释😃
让我们开始解释吧!
Photo by Possessed Photography on Unsplash
学习什么是图像分类
图像分类问题是从一组固定的类别中给一幅图像分配一个标签的任务。这是计算机视觉中的核心问题之一,尽管它很简单,却有大量的实际应用。用穷人的话说,你想要的是,如果你给计算机一个狗的图像,它告诉你“这是一只狗”。
这是一个可以使用人工智能和计算机视觉解决的问题。计算机视觉有助于操纵和预处理图像,使它们成为计算机可以使用的形式(从位图到相关值的矩阵)。一旦你有了一个好的输入形式,你就可以应用一个算法来预测结果。
现在最常见的解决方案是使用 CNN(卷积神经网络)。这种神经网络对于图像处理非常方便,并且根据您将提供的数据集进行训练。
数据集只是一个样本列表,每个样本都有标签。主要话题是你告诉机器如何通过例子来决定。通常,数据集分为训练集、测试集和验证集。这是因为您想要训练网络,然后测试它如何在单独的数据上工作,直到它按预期工作。最后,如果你想得到客观的反馈,你必须使用其他数据:验证集。这是必需的,因为如果你让网络总是在相同的数据上训练,它将丢弃任何错误,但是将能够只处理你提供的样本。所以,如果你投入一点点不同的东西,你会想要得到一个好的结果。这被称为过度拟合,是应该避免的,因为这意味着网络没有抽象规则,而只是重复你告诉它的东西。想想一个数学表达式 2*5+10,就像记住结果是 25,而不是能够计算它。
理解卷积神经网络
在本节中,我们将了解什么是 CNN 及其工作原理。下一张图是网络的图形表示。这将在下面的章节中解释,但是现在,你可以把 CNN 想象成一个预处理管道,为最终的神经网络润色数据。我知道,这个定义可能看起来很胭脂,但是让我们跳到下一段,详细看看它是如何工作的!
A sample architecture for a CNN. made with ❤️ by the Daniele Fontani
输入
在图像分类中,我们从…图像开始!不难理解,图像是由像素组成的二维矩阵(宽*高)。每个像素由 RGB、红、绿、蓝三种不同的值组成。使用 CNN 可以方便的分离 3 个不同的图层,所以你最终的输入矩阵将会是 image_size x image_size x 3 。当然,如果你有一个黑色的&白色的图像,你不需要 3 层,只需要一层,所以你会有 *image_size x image_size x 1。*嗯,如果你也考虑到你的数据集将由 N 项组成,整个输入矩阵将是 N x 图像大小 x 图像大小 x 3 。图像的大小必须是相同的,不能是全高清图像,以避免太长的时间处理。这方面没有成文的规定,但通常是一种妥协:在某些情况下,256x256 可能是一个不错的值,换句话说,您将需要更高的分辨率。
卷积
在图像内部,许多细节、暗示和阴影与网络无关。所有这些细节可能会混淆训练,因此主要思想是简化图像,保留带来信息的所有数据。这很直观,在文字上很容易,但在实践中呢?CNN 使用卷积步骤,这是这种方法的核心,来减小图像的大小,保留图像中更相关的部分。卷积层之所以有这个名字,是因为它在矩阵的滑动部分和过滤器之间进行卷积。过滤器的大小和要分析的矩阵块是相同的。这一块叫内核。为了使矩阵大小适合内核大小,在所有维度上都用零填充。卷积为所有内核乘法生成一个标量值。这将产生尺寸下降,例如,在 32x32 矩阵(1024 个元素)上使用内核 4x4,您将在输出中得到 4x4 矩阵(16 个元素)。内核的大小会影响最终结果,通常最好保持小内核并链接多个卷积层,我可以在中间添加一些池层(我稍后会谈到池)。
The convoluton process. made with ❤️ by the Daniele Fontani
汇集
汇集层用于减少矩阵大小。有许多方法,但基本的是:我取一组相邻的值,我只用了一个。最常见的算法是最大池,所以,基本上,你把更大的元素放入集合。
The max pool layer. made with ❤️ by the author
完全连接的层
最后一步是神经网络。直到这最后一步,我们已经做了一些“确定性”的运算,只是代数计算。这一步,我们有了真正的人工智能。之前已经完成了所有工作,目的只是生成网络可以理解的数据。
结论
自从我第一次踏入这个领域,在 2008 年,就有了相关的变化。作为一个业余爱好者的第一感觉,就是现在所有的过程都是“确定性的”。通过使用标准技术和良好的文档,使网络工作变得更加容易。体验仍然很重要,我不想将人工智能与常规的数据库读/写操作进行比较,但找到大量的资料、教程、指南是让新手能够工作的事情。
这要归功于大玩家,像往常一样,他们让开发者可以访问 AI,共享他们的库,也许只是让我们知道把他们的所有东西作为服务来消费更容易。😃
给开发者,尤其是最年轻的开发者的大建议是,即使有很多实现神经网络的工具,也要首先专注于一个理论。当您将在库的基础上构建的系统将停止工作,并且您无法理解原因时,理解幕后的工作方式是非常重要的。
参考
- 我的原创文章发表于 2019 年 5 月 13 日*【https://www.codeproject.com】*。**
- 维基百科上的 CNN
- 维基百科上的图像分类
- KerasUI 的源代码
- 代码项目获奖
利用 SSIM 进行图像分类
用 OpenCV 实现简单的图像分类器
作为人类,我们通常非常善于发现图片中的差异。比如我们看看上面这张图,看看它们有什么不同。首先,水果、冰淇淋和饮料明显发生了变化。很简单,对吧?
然而,对于计算机来说,这并不是一件容易的事情。计算机只能从我们训练它的模型的东西中学习。有一些很棒的模型可以很好地分类批量图像,比如谷歌的 TensorFlow 和 Keras 。
多亏了像这样的图像分类器库,计算机视觉有了巨大的飞跃。现在我们可以创建复杂的模型,比如 kaggle 中的这个: Animals-10 ,它包含十种不同类型的动物以及已经训练和清洗过的非动物的数千张图片。你所要做的就是创建一个模型,看看你的模型能多好地预测每一种不同类型的动物。有关图像分类模型的教程,请查看 Prabhu 或 Amitabha 。
然而,我想创建一个图像分类器,可以告诉如何相似的两个图像。为此,不需要任何复杂的库,如 TensorFlow 或上面链接的图像分类模型。有两种方法可以发现一个图像是否与另一个图像相似。首先是看均方差 ( MSE )其次是结构相似指数 ( SSIM )。
Left is MSE while right is SSIM
他们看起来都很可怕,但没必要担心。由于 NumPy 和 SSIM 是 Sci-Kit 图像库的内置方法部分,所以 MSE 可以很容易地计算出来,所以我们可以加载它。
在我们做所有这些之前,让我们定义一下每个人在计算什么。MSE 将计算我们正在比较的两幅图像的每个像素之间的均方误差。而 SSIM 会反其道而行之,在像素中寻找相似之处;即两个图像中的像素是否对齐和/或具有相似的像素密度值。唯一的问题是 MSE 往往有任意高的数字,所以很难标准化。虽然一般来说,mse 越高,它们越不相似,但如果图片集之间的 MSE 随机出现差异,我们就很难区分任何东西。另一方面,SSIM 把每件事都放在-1 到 1 的范围内(但是我不能给出小于 0 的分数)。1 分意味着它们非常相似,而-1 分意味着它们非常不同。在我看来,这是一个更好的衡量标准。
现在让我们关注代码,并开始导入所有必需的库:
Loading up necessary libraries
我使用 CV2(OpenCV 的一部分)来阅读和编辑我的图片。如果你愿意,也可以用 matplotlib 的 imread 来代替。我的朋友 Yish Lim 在她的博客中为口袋妖怪比赛的相似性做了这件事,这真是太棒了。
现在是写 MSE 公式的可怕部分:
Woah, that was tough
因为 SSIM 已经通过 skimage 导入,所以不需要手工编码。现在我们创建一个函数,它将接收两幅图像,计算它的 mse 和 ssim,并一次显示所有的值。
现在,接下来的三个步骤可以用 for 循环一次完成,但让我们在下面的代码中将其分解,这样更容易理解:
I chose a purposely large picture size for better SSIM, but beware it takes some time to calculate
首先,我们加载保存在目录中的图像。第二,我们必须确保它们都是相同的尺寸,否则我们会得到一个尺寸误差。这样做的问题是会导致图像失真,所以继续玩下去,直到你找到完美的数字。接下来我们再做一个函数,这样我们就可以看到我们的图片是什么样子。
It looks very small, and I haven’t figured out how to increase the size
现在来测试一下,看看我们的 MSE 和 SSIM 是否可以通过比较一张图片来工作。如果可行,那么我们应该得到 0 的 MSE 和 1 的 SSIM。
Yes, it works!
既然我们知道我们的电脑可以判断正在比较的图片是否相同,那么我们比较不同的图片怎么样?为了简单起见,我就把三张狗的图片比作自己,三张猫的图片比作自己。
Left: Comparing three different dogs to each other. Right: Comparing three different cats to each other
让我们看看我们的简单算法刚刚比较了什么。如你所见,MSE 变化很大,所以很难区分什么是什么。但是看看 SSIM,我们可以看到狗 2 和狗 3 相对于其他的狗是最相似的。视觉上,我同意,特别是耳朵。但是我认为狗 1 和狗 3 由于它们的姿势会有更高的 SSIM。事实上,在图片的灰度循环之前,狗 2 和狗 3 的鼻子周围有相似的白色皮毛,而狗 1 没有。这很可能是狗 2 和 3 比狗 1 具有更高 SSIM 值的原因。对于猫来说,这有点困难。猫 1 和猫 2 有相似的形状,照片是从相似的距离拍摄的,但是猫 2 和猫 3 有相似颜色的毛。这可能就是为什么第 1 类和第 3 类与第 2 类和第 3 类评级相似,而第 1 类和第 2 类评级不同的原因。
我只想再进行两项测试。一个是关于一只狗在一只猫面前的样子,第二个是每只动物如何与原始源代码附带的 gate 图片进行比较。
Dog vs. Gate vs. Cat
正如所料,狗和猫是相似的,特别是与一个无生命的物体相比,如侏罗纪公园 1 入口大门(侧注:如此伟大的电影!).狗和猫在门图片上有高 SSIM 的唯一原因是因为它的大小和灰度过滤器。
当谈到调整图像大小和重新配置图像时,OpenCV 不是最好的。为此,谷歌的 TensorFlow 是最好的。然而,我在 TensorFlow 上遇到的问题是,我无法将单张图片加载到它们的图库模块中。TensorFlow 最适合批量图片。
对于未来,我希望使用我提到的 kaggle Animal-10 数据集和 TensorFlow 来运行一个完整的图像分类实验室。
感谢阅读。
我的 github 链接包含代码。
github.com](https://github.com/imamun93/Image-Similarities-using-SSIM) [## 如何:Python 比较两个图像- PyImageSearch
你能猜到我是一个集邮者吗?开玩笑的。我没有。但是让我们玩一个假装的小游戏…
www.pyimagesearch.com](https://www.pyimagesearch.com/2014/09/15/python-compare-two-images/)
基于 Tensorflow 2.0 的影像分类
对自定义图像进行分类的端到端流程
在这篇文章中,我们将解决一个最常见的人工智能问题。场景是这样的:你正在酒吧上网,喝着啤酒,吃着鸡翅,这时你开始想“我能使用 Tensorflow 2.0 和迁移学习编写一个图像分类器吗?”本文将展示实现这一点的端到端过程。注意,我不会深入研究这个模型是如何工作的,那将是另外一篇文章。本文将向您展示如何使用 Tensorflow 2.0。一旦我们有了它,我们就可以回过头来使用参数,这可能会使代码更有效。
唯一的先决条件是:
- 一台电脑(显然)。我在一台没有 GPU 的小型笔记本电脑上运行我的程序。
- 带摄像头的手机。我们将会生成我们自己的图像。
- (可选)云图像存储。我用了亚马逊照片。
- Docker,我们将用它来托管我们的 Tensorflow 2.0 环境。如果你没有,安装说明可以在这里找到
我们将遵循的步骤是:
- 安装 Tensorflow 2.0 Docker 镜像。
- 获取一组图像来训练/验证/测试我们的模型。
- 将我们的图像组织成适合我们模型的目录结构。
- 下载一个预先训练好的深度学习模型。
- 为我们的特定用例(啤酒或鸡翅)定制我们的模型。
- 训练我们的定制模型。
- 可视化模型预测。
安装 Tensorflow 2.0 Docker 映像
我按照 T2 页面上的指示做了。以下是我使用的具体命令。注意,对于本教程,图像必须包含 Jupyter。
docker pull tensorflow/tensorflow:2.0.0a0-py3-jupyter
首先,cd 到您将存储源代码的目录。从那里,我们将启动我们的映像。我创建了一个简单的 shell 脚本(注意,我是在 Linux 上运行的,如果你在 Mac 或 Windows 上,你可能不需要 sudo 命令:
sudo docker run-it-p 8888:8888-v $ PWD:/TF/data-w/TF/data tensor flow/tensor flow:2 . 0 . 0 A0-py3-jupyter
这里需要知道的最重要的事情是:
- -p 是端口映射,我们的 Jupyter 笔记本在 Docker 中的端口 8888 上运行,因此我们将在我们的机器上映射端口 8888 以进行匹配。只有当端口 8888 已经在您的机器上使用时,您才应该更改它。
- -v 是如何在 docker 中挂载一个卷。在本例中,我们将当前目录($ term)挂载为/tp/data。这个 Docker 映像中的 jupyter 笔记本运行在/tp 目录中,因此当您打开 Jupyter 时,应该会看到以下内容:
其中数据目录映射到机器上的当前目录。
运行上述命令后,您将在命令提示符下看到类似这样的内容。
这里重要的一行是“Jupyter 笔记本运行于”之后的一行。您需要复制以“:8888”开头的那一行。然后在你的浏览器中输入 http://localhost 并粘贴你复制的行。在这种情况下:
http://localhost:8888/?token=cd37ab44fab55bce5e44ac6f4bb187a4b34b713c5fbeac9e
至此,Tensorflow 2.0 已经在 Docker 容器中启动并运行,可以访问您的本地文件系统。
获取一组图像来训练/验证/测试我们的模型
这一步很容易。我用手机从不同角度拍了大约 30 张啤酒杯的照片,从不同角度拍了 30 张鸡翅的照片。我是亚马逊 Prime 的大用户,所以我把手机设置成把我的照片备份到亚马逊照片。你可以使用任何你喜欢的云环境(ICloud,Google photos 等),甚至可以用电子邮件给自己发照片。这里的重点是把照片复制到你的电脑上。
将我们的图像组织成适合我们模型的目录结构
第一步是给我们的图像贴标签。有几种方法可以做到这一点,但最终,你会想把所有的“啤酒”图片复制到一个名为“啤酒”的目录中,把你的“翅膀”图片复制到一个名为“翅膀”的目录中。从这里开始,您需要创建一个如下所示的目录结构:
以下是我在 Linux 中使用的命令:
mkdir train
mkdir test
mkdir val
mkdir train/beer
mkdir train/wings
mkdir test/beer
mkdir test/wings
mkdir val/beer
mkdir val/wings
此时,您需要将数据的子集移动到 val 和 test 目录中。经过一些谷歌搜索,我找到了这个命令:
shuf -n 6 -e * | xargs -i mv {} target-directory
我使用以下命令实现了这一点:
cd beer
shuf -n 6 -e * | xargs -i mv {} ../test/beer
shuf -n 6 -e * | xargs -i mv {} ../val/beer
mv * ../train/beer
cd ..
cd wings
shuf -n 6 -e * | xargs -i mv {} ../test/wings
shuf -n 6 -e * | xargs -i mv {} ../val/wings
mv * ../train/wings
这段代码将 6 幅图像分别移动到我们的 val 和 test 文件夹,其余的移动到我们的 train 文件夹。完成这些步骤后,您的目录结构应该如下所示:
下载预先训练好的深度学习模型
此时,在浏览器中返回到 Jupyter 笔记本,并创建一个新笔记本。首先,我们需要导入将要使用的 Python 库:
import numpy as np
import tensorflow.keras
from tensorflow.keras.models import Sequential, Model
from tensorflow.keras.layers import Dropout, Input
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.metrics import categorical_crossentropy
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import itertools
import matplotlib.pyplot as plt
%matplotlib inline
**重要的故障排除步骤:**您可能会得到缺少库的错误。根据 Docker 映像的版本,您可能需要运行以下步骤:
!pip install --upgrade pip
!pip install pillow
!pip install scipy
!pip install pandas
运行之后,您需要点击重启内核按钮并重新运行导入语句。
现在我们已经完成了 Python 导入,我们需要为每个图像文件夹生成 ImageGenerator 对象。图像生成器获取输入图像,并对其稍加修改,以提供一致性和形状来训练神经网络。请注意,我们的图片将是 224x224。
train_path = '/tf/data/beer_wings/train'
valid_path = '/tf/data/beer_wings/val'
test_path = '/tf/data/beer_wings/test'
train_batches = ImageDataGenerator().flow_from_directory(train_path, target_size=(224,224), classes=['beer', 'wings'], batch_size=32)
valid_batches = ImageDataGenerator().flow_from_directory(valid_path, target_size=(224,224), classes=['beer', 'wings'], batch_size=32)
test_batches = ImageDataGenerator().flow_from_directory(test_path, target_size=(224,224), classes=['beer', 'wings'], batch_size=32)
这是一个有用的函数,可以看看我们的图像生成器在做什么。我从一个非常有用的 Youtube 系列中找到了这个函数:
# plots images with labels within jupyter notebook
def plots(ims, figsize=(24,12), rows=4, interp=False, titles=None):
if type(ims[0]) is np.ndarray:
ims = np.array(ims).astype(np.uint8)
if (ims.shape[-1] != 3):
ims = ims.transpose((0,2,3,1))
f = plt.figure(figsize=figsize)
cols = len(ims)//rows if len(ims) % 2 == 0 else len(ims)//rows + 1
for i in range(len(ims)):
sp = f.add_subplot(rows, cols, i+1)
sp.axis('Off')
if titles is not None:
sp.set_title(titles[i], fontsize=32)
plt.imshow(ims[i], interpolation=None if interp else 'none')
imgs, labels = next(train_batches)
plots(imgs, titles=labels)
输出是:
注[0,1] =鸡翅,[1,0] =啤酒。
最后,我们准备下载我们的预训练模型。在这种情况下,我们将使用 VGG16 型号。Tensorflow 2.0 内置了众多模型。它们在这里被定义为。
这是导入预训练 VGG16 模型的代码:
vgg16_model = tensorflow.keras.applications.vgg16.VGG16(weights='imagenet', include_top=False, input_tensor=Input(shape=(224,224,3)))
就这么简单!嗯,差不多吧,重要的是我们设置 include_top = False,因为我们要创建自己的最终层,还要注意我们的输入形状是(224,224,3)。(224,224)与上面的图像生成器相匹配。多出来的 3 个是颜色通道(红、蓝、绿)。
为我们的特定用例(啤酒或鸡翅)定制我们的模型
现在我们已经下载了一个预训练的模型,它通常可以预测图像分类,让我们根据自己的需要定制它。从理论上讲,像这样的前几层模型简化了图像的部分,并识别出其中的形状。那些早期的标签非常普通(线条、圆环、正方形等等),所以我们不想重新训练它们。我们希望只训练网络的最后几层以及我们添加的新层。
首先,让我们禁用预训练模型中除最后 4 层以外的所有层的训练。
for layer in vgg16_model.layers[:-4]:
layer.trainable = False
现在,让我们将自己的最后几层添加到网络中:
# Create the model
model = Sequential()
# Add the vgg convolutional base model
model.add(vgg16_model)
# Add new layers
model.add(Flatten())
model.add(Dense(1024, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(2, activation='softmax'))
# Show a summary of the model. Check the number of trainable parameters
model.summary()
这就是我们基于 VGG16 定制的模型!
训练我们的定制模型
现在我们已经定义了模型,让我们编译它并训练它。
model.compile(loss='categorical_crossentropy',
optimizer=tensorflow.keras.optimizers.RMSprop(lr=1e-4),
metrics=['acc'])
history = model.fit_generator(
train_batches,
steps_per_epoch=train_batches.samples/train_batches.batch_size ,
epochs=5,
validation_data=valid_batches,
validation_steps=valid_batches.samples/valid_batches.batch_size,
verbose=1)
可视化模型预测
现在,让我们给训练好的模型输入一组它从未见过的图像。代码中最重要的部分是这两行:
test_imgs, test_labels = next(test_batches)predictions = model.predict(test_imgs)
第一个生成一批新的以前没有见过的图像。让我们看看我们的模型对这些图像的预测:
import pandas as pddef to_label(value):
if value==0:
return 'beer'
else:
return 'wings'test_imgs, test_labels = next(test_batches)
predictions = model.predict(test_imgs)
df = pd.DataFrame()
df['actual'] = test_labels[:,1]
df['predicted'] = np.round(predictions[:,1])df['predicted_labels']=df['predicted'].map(lambda x: to_label(x))
plots(test_imgs, titles=df['predicted_labels'])
结论
显然,这是一个实现图像分类的无意义的例子,但它确实提供了一些有价值的信息,可以应用于未来的项目。即数据获取、迁移学习和模型评估。请注意,代码可以很容易地修改,以允许多种分类(我们的例子只有 2 个)。
基于迁移学习的图像聚类
基于 Resnet50 + Kmeans 的猫狗图像聚类模型!!!!
聚类是无监督机器学习的一个有趣领域,我们将数据集分类到一组相似的组中。这是“无监督学习”含义的一部分,其中没有事先训练发生,数据集将是无标签的。可以使用不同的技术进行聚类,如 K-means 聚类、均值漂移聚类、DB 扫描聚类、层次聚类等。所有聚类算法背后的关键假设是特征空间中的邻近点具有相似的质量,并且它们可以被聚类在一起。
在本文中,我们将对图像进行聚类。图像也与常规 ML 中的数据点相同,可视为类似问题。但是最大的问题是,
定义图像的相似度!!!!!
相似性可以意味着看起来相似的图像,或者可以是相似的尺寸,或者可以是相似的像素分布,相似的背景等。对于不同的用例,我们必须导出特定的图像向量。即,包含图像实体(包含猫或狗)的图像向量将不同于具有像素分布的图像向量。
在这篇文章中,我们将有一组猫和狗的图片。我们会试着把它们聚类成猫照和狗照。为此,我们可以从预先训练的 CNN 模型(如 Resnet50)中导出图像向量。我们可以删除 resnet50 的最后一层,并提取 2048 大小的矢量。一旦我们有了向量,我们就在数据点上应用 KMeans 聚类。
这是我的数据集中的一些图片,大约有 60 张从网上随机抽取的猫狗图片。
代码遍历
第一步是加载所需的库并加载预训练的 Resnet50 模型。请记住从模型中移除最后一个 softmax 层。
resnet_weights_path = '../input/resnet50/resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5'
my_new_model = Sequential()
my_new_model.add(ResNet50(include_top=False, pooling='avg', weights=resnet_weights_path))
*# Say not to train first layer (ResNet) model. It is already trained*
my_new_model.layers[0].trainable = False
一旦我们加载了模型,我们可以有一个函数来加载所有的图像,将图像调整到固定的像素大小(224,224),通过模型传递它并提取特征集。
def extract_vector(path):
resnet_feature_list = []
for im in glob.glob(path):
im = cv2.imread(im)
im = cv2.resize(im,(224,224))
img = preprocess_input(np.expand_dims(im.copy(), axis=0))
resnet_feature = my_new_model.predict(img)
resnet_feature_np = np.array(resnet_feature)
resnet_feature_list.append(resnet_feature_np.flatten())
return np.array(resnet_feature_list)
一旦我们有了提取的特征集,我们就可以对数据集进行 KMeans 聚类。K 必须事先确定,或者我们可以画出损失函数与 K 的关系,并推导出它。由于我们知道 K 的值为 2,所以可以直接代入。
kmeans = KMeans(n_clusters=2, random_state=0).fit(array)
print(kmeans.labels_)
就这些!!!!我们已经完成了图像聚类模型。让我们看看,我们的模型能多好地聚集图像。
以下是与第一组相对应的一些图像:
这是另一个集群:
总体而言,集群性能似乎非常好。在我聚类的 60 个图像中,只有两个图像被错误地聚类。这些图片如下:
上面两只狗被错误的聚类成了猫。可能是 ML 模型觉得它们很像猫。😃
我们可以使用 t-SNE 算法进一步研究图像的分布。这是一种降维算法,其中 2048 图像向量将被降低到更小的维度,以便更好地绘图、存储和时间限制。下面是我得到的 60 个图像数据集的结果。
蓝点代表聚类-1(猫),绿点代表聚类-2(狗)。请注意,迷你照片不是 t-SNE 的一部分,它只是额外添加的。相交区域可以被认为是模型发现难以恰当地拟合聚类的地方。
结论
希望您对使用迁移学习构建一个基本的图像聚类方法有很好的理解。正如我已经说过的,在某些情况下,CNN 输出可能不是图像特征的最佳选择。我们也可以用 bagging 技术考虑 HSV(色调-饱和度-值)来创建向量,其中相似的像素分布是我们的聚类方法。
快乐学习:)
无监督学习:10 行 R 代码中的图像压缩
机器学习:无监督学习
使用主成分分析压缩图像的酷方法
Photo by Gaetano Cessati on Unsplash
主成分分析(PCA)是一种强大的机器学习工具。作为一种无监督的学习技术,它擅长于降维和特征提取。
但是,你知道我们可以用 PCA 来压缩图像吗?
在这篇文章中,我将介绍这个过程,并解释 PCA 如何用 10 行 R 代码压缩图像,最后描述了简单的数学运算。
Photo by Pietro Jeng on Unsplash
#安装包并加载库
#install.packages(“tidyverse”)
#install.packages(“gbm”)
#install.packages(“e1071”)
#install.packages(“imager”)
library(tidyverse)
library(tree)
library(randomForest)
library(gbm)
library(ROCR)
library(e1071)
library(imager)# load the dataset. This is a 100*100*1000 array of data. An array is a generalization of a matrix to more than 2 dimensions. The first two dimensions index the pixels in a 100*100 black and white image of a face; the last dimension is the index for one of 1000 face images. The dataset can be accessed at: [https://cyberextruder.com/face-matching-data-set-download/](https://cyberextruder.com/face-matching-data-set-download/).load(“faces_array.RData”)#PAC requires a single matrix. so, we need to transform the 100*100 matrix into a single vector (10,000). face_mat <- sapply(1:1000, function(i) as.numeric(faces_array[, , i])) %>% t# To visualize the image, we need a matrix. so, let's convert 10000 dimensional vector to a matrix
plot_face <- function(image_vector) {
plot(as.cimg(t(matrix(image_vector, ncol=100))), axes=FALSE, asp=1)
}plot_face(face_mat[, sample(1000, 1)])
这里,我们试图获得数据集的基本信息,并构造一个新的函数进行分析。
#查平均脸
face_average = colMeans(face_mat)plot_face(face_average)
很大程度上,我们可以把“平均脸”理解为其他图像的基线。通过在平均脸上加上或减去数值,我们可以得到其他的脸。
#以上代码不算 10 行的限制。#
#这是我们的 10 行代码#
# generate PCA results;
# scale=TRUE and center=TRUE --> mean 0 and variance 1
pr.out = prcomp(face_mat,center=TRUE, scale=FALSE)# pr.out$sdev: the standard deviations of the principal components;
# (pr.out$sdev)²: variance of the principal components
pr.var=(pr.out$sdev)² # pve: variance explained by the principal component
pve = pr.var/sum(pr.var) # cumulative explained variance
cumulative_pve <-cumsum(pve)**#see the math explanation attached in the end** U = pr.out$rotation
Z = t(pr.out$x)# Let's compress the 232nd face of the dataset and add the average face back and create four other images adopting the first 10,50,100, and 300 columns.
par(mfrow=c(1,5))
plot_face(face_mat[232,])
for (i in c(10,50,100,300))
{
plot_face((U[,1:i]%*%Z[1:i,])[,232]+face_average)
}
我们做到了!成绩还不算太差。最左边是原始图像,后面是四幅压缩图像。
简单的数学解释。
PCA 与矩阵的奇异值分解(SVD)密切相关。所以,x= ud(vt)=**z***(vt),
其中 x 是的 1000*10000 矩阵,
- v:特征向量的矩阵(由 prcomp 返回的旋转)
- d:主成分的标准偏差(由 prcomp 返回的 sdev)
- 所以, z = UD(旋转空间中主分量的坐标(prcomp$x))。
换句话说,我们可以使用 V 的前 k 列和 z 的前 k 列来压缩图像:
数学结束。
喜欢读这本书吗?
查看我关于人工智能和机器学习的其他帖子。
哪一种最适合不平衡数据?有什么权衡吗?
towardsdatascience.com](/classifying-rare-events-using-five-machine-learning-techniques-fab464573233)
使用 Python 进行影像数据分析:
本教程着眼于如何导入图像和观察它的属性,分裂层,还着眼于灰度。
内容:
- 简介:关于像素的一点点
- 观察图像的基本属性
- 灰度
- 使用逻辑运算符处理像素值
- 掩饰
- 图像处理
简介:关于像素的一点点
计算机将图像存储为微小方块的马赛克。这就像古代艺术形式的瓷砖马赛克,或者今天孩子们玩的熔珠工具。现在,如果这些方形瓷砖太大,就很难做出光滑的边缘和曲线。我们使用越多越小的瓷砖,图像就越平滑,或者说像素化程度越低。这些有时被称为图像的分辨率。
矢量图形是一种有点不同的存储图像的方法,旨在避免像素相关的问题。但即使是矢量图像,最终也显示为像素的马赛克。像素这个词的意思是一个图像元素。描述每个像素的简单方法是使用三种颜色的组合,即红、绿、蓝。这就是我们所说的 RGB 图像。
每张数码形式的照片都是由像素组成的。它们是组成图片的最小信息单位。通常为圆形或方形,它们通常排列成二维网格。
现在,如果这三个值都是最大强度,那就意味着它们是 255。然后它显示为白色,如果三种颜色都被静音,或者值为 0,则颜色显示为黑色。这三者的结合将依次给我们一个特定的像素颜色的阴影。因为每个数字都是 8 位数字,所以值的范围是从 0 到 255。
这三种颜色的组合趋于其中的最高值。因为每个值可以有 256 个不同强度或亮度值,所以总共有 1680 万种色调。
现在让我们加载一个图像,观察它的各种属性。
import imageio
import matplotlib.pyplot as plt
%matplotlib inline
pic = imageio.imread(‘images/me.jpg’)
plt.figure(figsize = (5,5))
plt.imshow(pic)
观察图像的基本属性
print('Type of the image : ' , type(pic))
print('Shape of the image : {}'.format(pic.shape))
print('Image Hight {}'.format(pic.shape[0]))
print('Image Width {}'.format(pic.shape[1]))
print('Dimension of Image {}'.format(pic.ndim))
输出:
Type of the image : <class 'imageio.core.util.Array'>
Shape of the image : (728, 720, 3)
Image Hight 728
Image Width 720
Dimension of Image 3
阵列的形状表明它是一个三层矩阵。这里的前两个数字是长度和宽度,第三个数字(即 3)用于三层:红色、绿色、蓝色。因此,如果我们计算一个 RGB 图像的大小,总的大小将被计算为高 x 宽 x 3
print('Image size {}'.format(pic.size))
print('Maximum RGB value in this image {}'.format(pic.max())) print('Minimum RGB value in this image {}'.format(pic.min()))
输出:
Image size 1572480
Maximum RGB value in this image 255
Minimum RGB value in this image 0 # A specific pixel located at Row : 100 ; Column : 50
# Each channel's value of it, gradually R , G , B
print('Value of only R channel {}'.format(pic[ 100, 50, 0])) print('Value of only G channel {}'.format(pic[ 100, 50, 1])) print('Value of only B channel {}'.format(pic[ 100, 50, 2]))
输出:
Value of only R channel 168
Value of only G channel 189
Value of only B channel 182
好了,现在让我们快速浏览一下整个图像中的每个通道。
plt.title('R channel')
plt.ylabel('Height {}'.format(pic.shape[0]))
plt.xlabel('Width {}'.format(pic.shape[1]))
plt.imshow(pic[ : , : , 0])
plt.show()
plt.title('G channel')
plt.ylabel('Height {}'.format(pic.shape[0]))
plt.xlabel('Width {}'.format(pic.shape[1]))
plt.imshow(pic[ : , : , 1])
plt.show()
plt.title('B channel')
plt.ylabel('Height {}'.format(pic.shape[0]))
plt.xlabel('Width {}'.format(pic.shape[1]))
plt.imshow(pic[ : , : , 2])
plt.show()
现在,我们还可以改变 RGB 值的数量。作为一个例子,让我们设置红色,绿色,蓝色层的后续行值为全强度。
- r 通道:第 100 至 110 行
- g 通道:第 200 至 210 行
- b 通道:行— 300 至 310
我们将加载图像一次,这样我们可以同时看到每个变化。
pic[50:150 , : , 0] = 255 # full intensity to those pixel's R channel
plt.figure( figsize = (5,5))
plt.imshow(pic)
plt.show()
pic[200:300 , : , 1] = 255 # full intensity to those pixel's G channel
plt.figure( figsize = (5,5))
plt.imshow(pic)
plt.show()
pic[350:450 , : , 2] = 255 # full intensity to those pixel's B channel
plt.figure( figsize = (5,5))
plt.imshow(pic)
plt.show()
为了使它更清楚,让我们改变列部分,这一次我们将同时改变 RGB 通道。
# set value 200 of all channels to those pixels which turns them to white
pic[ 50:450 , 400:600 , [0,1,2] ] = 200
plt.figure( figsize = (5,5))
plt.imshow(pic)
plt.show()
拆分层
现在,我们知道图像的每个像素由三个整数表示。将图像分割成单独的颜色分量仅仅是取出图像阵列的正确切片的问题。
import numpy as np
pic = imageio.imread('images/me.jpg')
fig, ax = plt.subplots(nrows = 1, ncols=3, figsize=(15,5))
for c, ax in zip(range(3), ax):
# create zero matrix
split_img = np.zeros(pic.shape, dtype="uint8")
# 'dtype' by default: 'numpy.float64' # assing each channel
split_img[ :, :, c] = pic[ :, :, c] # display each channel
ax.imshow(split_img)
灰度
黑白图像存储在二维数组中。有两种类型的黑白图像:
- Binary: Pixel is either black or white:0 or 255
- Greyscale: Ranges of shades of grey:0 ~ 255
现在,灰度化是一个将图像从全色转换为灰色阴影的过程。例如,在图像处理工具中:在 OpenCV 中,许多函数在处理之前使用灰度图像,这样做是因为它简化了图像,几乎起到了降噪的作用,并增加了处理时间,因为图像中的信息较少。
在 python 中有几种方法可以将图像转换为灰度,但使用 matplotlib 的一种直接方法是使用此公式对原始图像的 RGB 值进行加权平均。
Y’ = 0.299 R + 0.587 G + 0.114 B
pic = imageio.imread('images/me.jpg')
gray = lambda rgb : np.dot(rgb[... , :3] , [0.299 , 0.587, 0.114]) gray = gray(pic) plt.figure( figsize = (5,5))
plt.imshow(gray, cmap = plt.get_cmap(name = 'gray'))
plt.show()
使用逻辑运算符处理像素值
我们可以使用逻辑运算符创建一个相同大小的金块数组。然而,这不会创建任何新的数组,只是将 True 返回给它的主机变量。例如,假设我们想要过滤掉 RGB 图像中的一些低值像素或高值像素或(任何条件),是的,将 RGB 转换为灰度是很好的,但目前,我们不会这样做,而是处理彩色图像。
让我们首先加载一个图像并在屏幕上显示它。
pic = imageio.imread('images/logic_op_pic.JPG')
plt.figure(figsize=(5,5))
plt.imshow(pic)
plt.show()
让我们考虑这个转储图像。现在,在任何情况下,我们都要过滤掉所有的像素值,假设低于 20。为此,我们将使用一个逻辑操作符来完成这项任务,我们将为所有索引返回 True 值。
low_pixel = pic < 20
# to ensure of it let's check if all values in low_pixel are True or not
if low_pixel.any() == True:
print(low_pixel.shape)
输出:
(743, 911, 3)
正如我们所说的,主机变量传统上不被使用,但是我引用它是因为它的行为。它只包含真正的值,没有其他内容。所以,如果我们同时看到 low_pixel 和 pic 的形状,我们会发现两者的形状是一样的。
print(pic.shape)
print(low_pixel.shape)
输出:
(743, 911, 3)
(743, 911, 3)
我们使用全局比较运算符为所有小于 200 的值生成了低值过滤器。但是,我们可以使用这个 low_pixel 数组作为索引,将那些低值设置为一些特定的值,这些值可能高于或低于前面的像素值。
# randomly choose a value
import random
# load the orginal image
pic = imageio.imread('images/logic_op_pic.JPG')
# set value randomly range from 25 to 225 - these value also randomly choosen
pic[low_pixel] = random.randint(25,225)
# display the image
plt.figure( figsize = (5,5))
plt.imshow(pic)
plt.show()
掩饰
图像遮罩是一种图像处理技术,用于从具有模糊边缘、透明或毛发部分的照片中移除背景。
现在,我们将创建一个圆盘形状的遮罩。首先,我们将测量从图像中心到每个边界像素值的距离。我们取一个方便的半径值,然后使用逻辑运算符,我们将创建一个圆盘。挺简单的,看看代码吧。
# Load the image
pic = imageio.imread('images/logic_op_pic.JPG')
# seperate the row and column values
total_row , total_col , layers = pic.shape
''' Create vector. Ogrid is a compact method of creating a multidimensional ndarray operations in single lines.
for ex:
>>> ogrid[0:5,0:5]
output: [array([[0],
[1],
[2],
[3],
[4]]),
array([[0, 1, 2, 3, 4]])]
'''
x , y = np.ogrid[:total_row , :total_col]
# get the center values of the image
cen_x , cen_y = total_row/2 , total_col/2 '''
Measure distance value from center to each border pixel. To make it easy, we can think it's like, we draw a line from center- to each edge pixel value --> s**2 = (Y-y)**2 + (X-x)**2
'''
distance_from_the_center = np.sqrt((x-cen_x)**2 + (y-cen_y)**2)
# Select convenient radius value
radius = (total_row/2)
# Using logical operator '>'
'''
logical operator to do this task which will return as a value of True for all the index according to the given condition
'''
circular_pic = distance_from_the_center > radius '''
let assign value zero for all pixel value that outside the cirular disc. All the pixel value outside the circular disc, will be black now.
''' pic[circular_pic] = 0
plt.figure(figsize = (5,5))
plt.imshow(pic)
plt.show()
图像处理
关于 edX 的 MOOC 课程之一,我们已经介绍了一些卫星图像及其处理系统。这当然是非常有益的。但是,让我们对它进行一些分析
这幅图像有一些有趣的地方。像许多其他可视化一样,每个 RGB 层中的颜色都有一定的含义。例如,红色的强度将指示像素中地理数据点的高度。蓝色的强度表示坡向的度量,绿色表示坡度。这些颜色将有助于以更快、更有效的方式传达这些信息,而不是显示数字。
Red pixel indicates: Altitude
Blue pixel indicates: Aspect
Green pixel indicates: Slope
仅仅通过看这张彩色的图像,一只训练有素的眼睛就能分辨出高度、坡度和方位。所以,这就是给这些颜色赋予更多意义的想法,以表明一些更科学的东西。
# Only Red Pixel value , higher than 180
pic = imageio.imread('images/sat_img.JPG')
red_mask = pic[:, :, 0] < 180
pic[red_mask] = 0
plt.figure(figsize=(5,5))
plt.imshow(pic)
# Only Green Pixel value , higher than 180
pic = imageio.imread('images/sat_img.JPG')
green_mask = pic[:, :, 1] < 180
pic[green_mask] = 0
plt.figure(figsize=(5,5))
plt.imshow(pic)
# Only Blue Pixel value , higher than 180
pic = imageio.imread('images/sat_img.JPG')
blue_mask = pic[:, :, 2] < 180
pic[blue_mask] = 0
plt.figure(figsize=(5,5))
plt.imshow(pic)
# Composite mask using logical_and
pic = imageio.imread('images/sat_img.JPG')
final_mask = np.logical_and(red_mask, green_mask, blue_mask)
pic[final_mask] = 40
plt.figure(figsize=(5,5))
plt.imshow(pic)
记住你可以在我的 github 库这里找到完整的工作代码。
感谢的阅读,我很高兴讨论你可能有的任何问题或纠正:)如果你想讨论机器学习或其他任何问题,请在 LinkedIn 上找到我。
Keras 中的图像数据生成器
如何有效和高效地使用 Keras 中的数据生成器进行深度学习的计算机视觉应用
我做过学术研究员,目前在业内做研究工程师。到目前为止,我在这两个角色中的经验告诉我,不能过分强调数据生成器对于培训的重要性。从只有 6 万张训练图像的 MNIST 数据集到拥有超过 1400 万张图像的 ImageNet 数据集[1],数据生成器将是深度学习训练和推理的无价工具。使用数据生成器的几个主要优势如下:
- **允许使用多处理:**在多个 CPU 内核上并行化加载过程,以加快进程。
- **允许你生成批次:**你可以使用更小的数据块通过批次梯度下降来训练你的模型。实际上,所有与深度学习相关的真实世界数据集通常都无法一次放入内存,所以大多数时候这是唯一可能的解决方案。
- **允许您进行数据增强:**不管您的数据是否有限,也不管您是否希望在噪声增强方面增加多样性,您通常都会这样做。通过使用现代 CPU 的多处理能力,数据生成器有助于静态和动态地完成这些工作。
- **不需要编写样板代码:**大多数深度学习框架都支持他们的数据加载器/数据生成器版本,这减少了您必须编写样板代码来处理批量数据的创建及其并行化增加的时间。这也在代码中引入了一致性和可读性。
在本文中,我将讨论如何使用 Keras中的数据生成器进行图像处理相关的应用,并分享我在研究期间使用的技术。
Keras 中的数据生成器[2]
Keras 有适用于不同数据类型的 DataGenerator 类。然而,正如我前面提到的,这篇文章是关于图像的,对于这个数据来说,ImageDataGenerator 是相应的类。
我将使用代码来解释这个过程,因为我相信这会导致更好的理解。我将涉及六个方面。
- 创建数据生成器
- 一些有用的数据生成器属性
- 用于快速正确性测试的可视化数据生成器张量
- 使用数据生成器进行培训
- 使用数据生成器进行预测
- 培训、验证和测试集创建
1.创建数据生成器
我们从本教程所需的导入开始。这涉及 ImageDataGenerator 类和其他一些可视化库。
创建生成器包括两个主要步骤。
- 用必需的参数实例化 ImageDataGenerator 以创建对象
- 根据数据在磁盘上的存储方式,使用适当的 flow 命令(稍后将详细介绍)。这个命令将允许您动态地生成和访问批量数据。
下面显示了实现上述两个步骤的示例代码。
我们从指定批量大小的第一行代码开始。我们将其设置为 32,这意味着一批图像将有 32 个图像以张量形式堆叠在一起。这个数组的形状应该是(batch_size,image_y,image_x,channels)。这是信道最后的方法,即信道的数量在最后的维度中。
字典中为 ImageDataGenerator 构造函数指定的参数很少。下面将对它们进行解释。[2]
- 旋转 _ 范围:整数。随机旋转的度数范围。
- height_shift_range :沿高度方向移动图像。它支持各种输入。对于浮动,如果<为 1,图像将移动总高度的
部分,或者如果> = 1,图像将移动像素。 - width_shift_range :沿宽度方向移动图像。
- 重新调整:重新调整因子。默认为无。如果无或为 0,则不应用重缩放,否则我们将数据乘以所提供的值(在应用所有其他变换之后)。
- fill_mode :为{“constant “、” nearest “、” reflect “或” wrap”}之一。默认值为“最近”。根据给定的模式填充输入边界外的点。
除了上述论点之外,还有其他几个论点。这些允许你在向你的网络提供数据的时候动态地扩充你的数据。有关更多详细信息,请参考文档[2]。
然后,使用 python 关键字参数将这些参数传递给 ImageDataGenerator,我们创建 datagen 对象。下一步是使用这个对象的 flow_from _directory 函数。当您将映像组织到操作系统上的文件夹中时,可以使用这种方法。
目录结构应该如下所示。
Source: From author
数据目录应该为每个类包含一个文件夹,该文件夹与该类以及该特定类的所有训练样本具有相同的名称。在上面的例子中,每个类有 k 个类和 n 个类。这使得样本总数 nk 。虽然每个类可以有不同数量的样本。
flow_from_directory 函数的参数解释如下。[2]
- 目录:字符串,目标目录的路径。每个类应该包含一个子目录。
- 类:可选的类子目录列表(如
['dogs', 'cats']
)。如果你只需要直接在中的几个类,只需要把它们指定为一个列表。顺序事项和类别索引按照列表顺序分配。 - class_mode :“分类”、“二元”、“稀疏”、“输入”或无之一。默认:“分类”。这决定了生成器返回的标签类型。
‣**‘分类’用于多个类。标签是一种热编码。
‣‘二进制’**是两个类。
‣ **“稀疏”**返回 1D 整数标签
‣ **“输入”**这对于自动编码器非常有用,因为您需要输入图像是标签
‣ **“无”**将导致生成器不返回任何标签 - target_size :整数元组
(height, width)
,默认:(256, 256)
。找到的所有图像将被调整到的尺寸。 - 插值:如果目标尺寸与加载的图像尺寸不同时,用于对图像重新取样的插值方法。“lanczos”在你缩小图片时很有用。在这个例子中,这就是我使用“lanczos”的原因。
在本教程中,我使用的是可描述的纹理数据集[3],它可以在这里找到。它包含 47 节课,每节课 120 个例子。所有的图像都有不同的尺寸。flow_from_directory 的 target_size 参数允许您创建大小相等的批。如果数据集包含不同大小的图像,这将非常方便。
2.一些有用的数据生成器属性
接下来,我们看看我们刚刚创建的数据生成器的一些有用的属性和函数。“样本”给出了数据集中可用图像的总数。’ class _ indices '给出了类名到整数映射的字典。这些是非常重要的,因为当你做预测的时候你会需要这个。因为当你做预测时,你将得到类别号,除非你知道映射,否则你将不能区分哪个是哪个。
Source: From author
Source: From author
“文件名”给出了目录中所有文件名的列表。如果您想要分析模型在几个选定样本上的性能,或者想要将输出概率直接分配给样本,这将非常有用。
Source: From author
datagenerator 对象是一个 python 生成器,在每一步都生成(x,y)对。在 python 中,应用于生成器的 next()从生成器生成一个样本。
Source: From author
正如所料,( x,y)都是 numpy 数组。图像批次是具有(128,128,3)维的 32 个样本的 4d 阵列。标签是一个具有(32,47)形状的热编码矢量。一种热编码意味着将类别号编码为长度等于类别数的向量。除了样本所属的类别之外,所有类别的向量都为零。因此,对于一个三类数据集,来自类 2 的样本的一个热向量将是[0,1,0]。
3.用于快速正确性测试的可视化数据生成器张量
因为我们现在有了一个批次和它的标签,我们将观察并检查是否一切都如预期的那样。
我已经编写了一个网格绘图实用函数,它可以绘制整洁的图像网格,并有助于可视化。它接受输入 image_list 作为图像列表或 numpy 数组。 nrows 和 ncols 分别是结果网格的行和列。
下面显示了一个可视化示例。
Source: https://www.robots.ox.ac.uk/~vgg/data/dtd/
我们看到图像如预期的那样随机旋转,并且填充是最近的,它重复有效帧中最近的像素值。图像也在水平和垂直方向上随机移动。它们都被调整到(128,128)的大小,并且它们保留它们的颜色值,因为颜色模式是“rgb”。
在这个阶段,你应该观察几个批次,确保样品看起来像你想要的样子。转换工作正常,没有任何不希望的结果。例如,如果对包含手写数字的 MNIST 数据集应用垂直翻转,9 将变成 6,反之亦然。这将损害训练,因为即使对于正确的预测,模型也会受到惩罚。
接下来,让我们比较一下与原始图像相比,图像批次是如何出现的。为此,我们将 shuffle 设置为 False,并创建另一个生成器。这允许我们将文件名映射到由数据生成器生成的批处理。datagenerators 有一个 reset()方法,可以将它重置为第一批。因此,每当您想要将模型输出与文件名相关联时,您需要将 shuffle 设置为 False,并在执行任何预测之前重置数据生成器。这将确保我们的文件被正确读取,并且没有任何错误。
Source: From author
我们可以看到原始图像具有不同的大小和方向。我们批量获得增强图像。
4.使用数据生成器进行培训
接下来,让我们继续讨论如何使用数据生成器来训练模型。这就是 Keras 的亮点,它提供了这些训练抽象,可以让你快速训练你的模型。这对快速成型非常有利。并且训练样本将使用多处理(如果启用的话)动态生成,从而使训练更快。
我将解释所使用的论点。[2]
- steps_per_epoch :整数。在宣布一个时期结束并开始下一个时期之前,从
generator
开始产生的总步骤数(样品批次)。它通常应该等于ceil(num_samples / batch_size). This ensures that the model sees all the examples once per epoch.
- 历元:整数。训练模型的时期数。按照
steps_per_epoch
的定义,一个历元是对所提供的全部数据的迭代。 - 工人:整数。使用基于进程的线程时要加速运行的最大进程数。如果未指定,
workers
将默认为 1。如果为 0,将在主线程上执行生成器。 - 使用 _ 多重处理:布尔型。如果
True
,使用基于进程的线程。如果未指定,use_multiprocessing
将默认为False
。注意,因为这个实现依赖于多重处理,所以您不应该将不可选择的参数传递给生成器,因为它们不容易传递给子进程。 - 验证 _ 数据:这可以是下面的:
‣验证数据的生成器或对象Sequence
‣元组(x_val, y_val)
‣元组(x_val, y_val, val_sample_weights)
在每个时期结束时在其上评估损失和任何模型度量。该模型不会根据此数据进行训练。 - 验证 _ 步骤:仅当
validation_data
为发电机时相关。在每个时期结束时停止之前,从validation_data
发生器产生的总步骤数(样品批次)。它通常应等于认证数据集的样本数除以批次大小。Sequence
可选:如果未指定,将使用len(validation_data)
作为多个步骤。
workers 和 use_multiprocessing 函数允许您使用多重处理。一次只指定其中一个。请记住将该值设置为 CPU 上的核心数,否则如果您指定一个更高的值,将会导致性能下降。
5.使用数据生成器进行预测
Keras 使得使用数据生成器进行预测变得非常简单和直接。
它有相同的多重处理参数可用。
6.培训、验证和测试集创建
本文最后一部分将集中在训练、验证和测试集创建上。这可以通过两种不同的方式实现。
- **第一种方法是创建三个独立的目录,并创建三个不同的数据生成器。**关于如何以可重复的方式进行分割,请参考【4】。这是一个简单的选择,因为你知道训练集、val 集和测试集中有哪些样本。
- **第二种方法是在 ImageDataGenerator 构造函数中使用“validation_split”参数。**仅当每个类别有一个包含训练和验证样本的文件夹,而不是有两个不同的目录时,才使用此参数。但是在这种情况下,您仍然有一个单独的测试集目录。此外,您必须为 flow_from_directory 函数使用 subset 参数。这些论点解释如下。
‣ 验证 _ 拆分:浮点。保留用于验证的图像比例(严格介于 0 和 1 之间)。因此,如果使用值 0.2,那么将为验证集保留 20%的样本,为训练集保留剩余的 80%。
‣ 子集:如果ImageDataGenerator
中设置了validation_split
,则为数据的子集("training"
或"validation"
第二种方法的代码如下所示,因为第一种方法很简单,已经在第 1 节中介绍过了。
Source: From author
由于我将 validation_split 值指定为 0.2,因此 20%的样本(即 1128 幅图像)被分配给验证生成器。训练和验证生成器是在 flow_from_directory 函数中用 subset 参数标识的。
关于 Keras 中数据生成器的教程到此结束。希望到现在为止,您已经对 Keras 中的数据生成器有了更深的理解,为什么这些很重要,以及如何有效地使用它们。
我已经在下面的库中提供了代码。
https://github.com/msminhas93/KerasImageDatagenTutorial
如果您发现任何错误或面临任何困难,请不要犹豫,通过 LinkedIn 或 GitHub 与我联系。
感谢您阅读帖子。快乐学习!
参考
[2]https://keras.io/preprocessing/image/
https://www.robots.ox.ac.uk/~vgg/data/dtd/
https://cs230.stanford.edu/blog/split/
基于 MCMC 的图像去噪
用马尔可夫链蒙特卡罗结合伊辛模型清除噪声二值图像
在这篇文章中,我将演示使用马尔可夫链蒙特卡罗去噪二值图像。
马尔可夫链蒙特卡罗(简称 MCMC)是指一类通过抽样来估计概率分布的技术。基于用于绘制样本的方法,构成 MCMC 的各种技术彼此不同。一些更著名的 MCMC 技术是 Metropolis-Hastings、Gibbs 抽样和 Hamiltonian 蒙特卡罗。我将使用的技术是吉布斯抽样。
Gibbs 抽样是在所有其他变量保持不变的情况下,从多元分布中进行抽样的一种方法。
例如,如果分布只有两个变量 x1 和 x2,则抽样如下:
Example distribution
它从点 1 开始,然后当要对第二个点进行采样时,它是这样的:
P(x2|x1),它在与 x1 相同的行上寻找下一个样本(保持不变)
随后第三个点采样如下:
P(x1|x2),它在与 x2 相同的线上寻找下一个样本(保持不变)
依此类推,采样过程针对设定数量的点继续进行,从而允许其遍历整个空间。
Ising Model
伊辛模型是对应于用于模拟相变的正方形晶格的数学模型。晶格中的每个元素可以以两种离散状态存在,可以用+1 和-1 来表示。每个元素对其所有相邻元素施加影响,并试图达到一种平衡状态,在这种状态下,所有元素都以相同的状态存在。
应用:
Conceptualized model of image
二进制图像可以被认为是格子的形式,每个像素代表一个元素。根据像素的颜色,像素的状态可以表示为 1 或-1。图像可以想象为由两层组成,下面的层代表真实的图像,上面的层代表噪声。高斯噪声被称为叠加在图像上,它在某些地方与实际图像匹配,而在某些地方取相反的值。
伊辛模型被应用于由噪声组成的上述层。噪声受每个相邻噪声的影响取决于它们之间的紧密程度,由边缘电位表示。他们越是紧密地联系在一起,就越是试图处于同一种状态。边缘电位的公式由下式给出:
exp(J,X ₐ ,X ₙ )
这里 J 是耦合常数,它表示邻居之间的联系有多紧密。Xₐ代表考虑中的像素,Xₙ代表其邻居的观察值。
高斯观察模型用于模拟噪声和预先存在的像素之间的关系。噪声被认为是实际基础像素值的函数,也是与其标准偏差的函数。它可以表示为:
N(Yₐ | Xₐ,σ)
这里,Yₐ代表像素的观察值,Xₐ代表考虑中的像素,σ代表标准差。
正是这两种力量的相互作用决定了像素的最终值。耦合常数试图保持所有相邻像素(噪声)处于相同的状态或颜色。高斯模型试图将像素的实际颜色与观察到的颜色(噪声)相匹配。这两种力的影响可以通过改变 J 和σ的值来控制。
所应用的吉布斯采样将根据其所有邻居和基础真值对每个像素进行采样。然后,它会修正这个值,继续处理下一个元素,并重复相同的操作。当它遍历完整个网格时,一次迭代就完成了。根据迭代次数,最终图像质量可能会有所不同。
Xₐ所考虑的像素可以取值+1 或-1。上面的表达式给出了 Xₐ取值+1 或-1 的可能性。
分子给出 Xₐ为例如+1 的可能性,并检查其与像素 Yₐ的观察值以及与其邻居 Xₙ.的观察值的关系
它除以分别取值为+1 和-1 的可能性之和,得出 Xₐ实际值为+1 的概率。
代码:
代码的目的是从损坏的图像中恢复原始图像。
Corrupted image
import numpy as np
import cv2
import random
import scipy
from scipy.spatial import distance
from scipy.stats import multivariate_normal
import pandas as pd
from PIL import Imagedata = Image.open('noise_img.png')
image = np.asarray(data).astype(np.float32)
图像以二维数组的形式导入并保存,其中包含像素的灰度值。为了便于操作,这个图像被转换成一个数组。
然后通过将所有 0(对应黑色)替换为-1,将所有 255(对应白色)替换为+1,将其转换为伊辛模型。数组的所有边都用 0 填充,以使在网格上迭代的任务更容易。
#Convert image values to ising model
for i in range(len(image)):
for j in range(len(image[0])):
if image[i,j,:] == 255:
image[i,j,:] = 1
else:
image[i,j,:] = -1#Create array to perform operations on
ising = np.zeros((len(image)+2,len(image[0])+2))for i in range(len(image)):
for j in range(len(image[0])):
ising[i+1,j+1] = image[i,j,:]
Image array
第一行和第一列是填充。其余的行和列分别对应于一个像素的颜色。行号和列号对应于像素的位置,单元值对应于颜色。
在开始吉布斯采样之前,为耦合强度设置一个值。
#Coupling strength
J=4#Gibbs sampling
for n in range(3):
for i in range(1,len(ising[0])-1):
for j in range(1,len(ising)-1):
pot = []
for x in [-1, 1]:
edge_pot = np.exp(J*ising[j-1,i]*x) * np.exp(J*ising[j,i-1]*x) * np.exp(J*ising[j+1,i]*x) * np.exp(J*ising[j,i+1]*x)
pot.append(edge_pot)
prob1 = multivariate_normal.pdf(image[j-1,i-1,:], mean = 1, cov = 1)*pot[1]/(multivariate_normal.pdf(image[j-1,i-1,:], mean = 1, cov = 1)*pot[1] + multivariate_normal.pdf(image[j-1,i-1,:], mean = -1, cov = 1)*pot[0])
if np.random.uniform() <= prob1:
ising[j,i] = 1
else:
ising[j,i] = -1
迭代原始数组中除零以外的所有值。通过检查原始颜色为+1 或-1 的可能性,对每个点进行采样。对于这两种情况,都计算了与其邻居的边缘电势。与边缘电位一起,观察值的可能性是相对于真实值+1 或-1 计算的。
基于以上两个值,像素为+1 的可能性通过将其除以像素为+1 和-1 的可能性来计算。
然后将该值与从标准正态分布中随机抽取的值进行比较。如果原始值为+1 的概率较高,则该值被设置为+1,否则被设置为-1。
当下一个元素被采样时,它在计算边沿电位时采用上述元素的新值。这段代码遍历整个点阵的每个元素三次。
#Retrieving the final array
final = np.zeros((len(image),len(image[0])))
final = ising[1:len(ising)-1,1:len(ising[0])-1]#Converting it back to image
for i in range(len(final[0])):
for j in range(len(final)):
if final[j,i] == 1:
final[j,i] = 255
else:
final[j,i] = 0
然后移除填充,然后将值转换回灰度值,然后可视化。
Cleaned image
最后,我们有去噪的图像。
你也可以通过 LinkedIn 与我联系。
Python 中的图像过滤器
我目前正在从事一个计算机视觉项目,我想研究图像预处理,以帮助改进我计划建立的机器学习模型。图像预处理包括对图像应用图像过滤器。本文将比较一些最著名的图像过滤器。
图像过滤器可用于减少图像中的噪声量并增强图像的边缘。图像中可能存在两种类型的噪声:斑点噪声和椒盐噪声。斑点噪声是在图像采集期间出现的噪声,而椒盐噪声(指稀疏出现的白色和黑色像素)是由图像信号中的突然干扰引起的。增强图像的边缘可以帮助模型检测图像的特征。
图像预处理步骤可以提高机器学习模型的准确性。当与在未经预处理的图像上训练的更复杂的模型相比时,预处理的图像可以帮助基本模型实现高精度。对于 Python,Open-CV 和 PIL 包允许你应用几个数字滤波器。应用数字滤波器需要将图像与内核(一个小矩阵)进行卷积。内核是一个n×n的方阵,其中 n 是奇数。内核依赖于数字滤波器。图 1 显示了用于 3 x 3 均值滤波器的内核。来自 http://kdef.se/ KDEF 数据集的一幅图像(可以在这里找到:)将用于数字滤波器示例。
Figure 1: A 3 x 3 mean filter kernel
1.均值滤波器
均值滤波器用于模糊图像,以消除噪声。它包括确定一个n×n内核中像素值的平均值。然后,中心元素的像素强度由平均值代替。这消除了图像中的一些噪声,并平滑了图像的边缘。Open-CV 库中的模糊函数可用于对图像应用均值滤镜。
当处理彩色图像时,首先需要从 RGB 转换到 HSV,因为 RGB 的维度相互依赖,而 HSV 中的三个维度相互独立(这允许我们分别对三个维度中的每一个应用过滤器。)
以下是均值过滤器的 python 实现:
import numpy as npimport cv2from matplotlib import pyplot as pltfrom PIL import Image, ImageFilter%matplotlib inlineimage = cv2.imread('AM04NES.JPG') # reads the imageimage = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # convert to HSVfigure_size = 9 # the dimension of the x and y axis of the kernal.new_image = cv2.blur(image,(figure_size, figure_size))plt.figure(figsize=(11,6))plt.subplot(121), plt.imshow(cv2.cvtColor(image, cv2.COLOR_HSV2RGB)),plt.title('Original')plt.xticks([]), plt.yticks([])plt.subplot(122), plt.imshow(cv2.cvtColor(new_image, cv2.COLOR_HSV2RGB)),plt.title('Mean filter')plt.xticks([]), plt.yticks([])plt.show()
Figure 2: The result of applying a mean filter to a color image
图 2 显示,虽然一些散斑噪声已经减少,但是图像中现在存在许多以前不存在的伪像。我们可以检查在对灰度图像应用均值滤波时是否产生了任何伪像。
# The image will first be converted to grayscale
image2 = cv2.cvtColor(image, cv2.COLOR_HSV2BGR)image2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)figure_size = 9new_image = cv2.blur(image2,(figure_size, figure_size))plt.figure(figsize=(11,6))plt.subplot(121), plt.imshow(image2, cmap='gray'),plt.title('Original')plt.xticks([]), plt.yticks([])plt.subplot(122), plt.imshow(new_image, cmap='gray'),plt.title('Mean filter')plt.xticks([]), plt.yticks([])plt.show()
Figure 3: The result of applying a mean filter to a grayscale image
图 3 显示均值滤波去除了一些噪声,并且不会为灰度图像产生伪像。然而,一些细节已经丢失。
2.高斯滤波器
高斯滤波器类似于均值滤波器,但是它涉及周围像素的加权平均,并且具有参数σ。核表示高斯分布的离散近似。虽然高斯滤波器会模糊图像的边缘(像均值滤波器一样),但它在保留边缘方面比类似大小的均值滤波器做得更好。Open-CV 软件包中的“GaussianBlur”函数可用于实现高斯滤波器。该函数允许您指定内核的形状。您也可以分别指定 x 和 y 方向的标准偏差。如果只指定了一个西格玛值,那么它被认为是 x 和 y 方向的西格玛值。
new_image = cv2.GaussianBlur(image, (figure_size, figure_size),0)plt.figure(figsize=(11,6))plt.subplot(121), plt.imshow(cv2.cvtColor(image, cv2.COLOR_HSV2RGB)),plt.title('Original')plt.xticks([]), plt.yticks([])plt.subplot(122), plt.imshow(cv2.cvtColor(new_image, cv2.COLOR_HSV2RGB)),plt.title('Gaussian Filter')plt.xticks([]), plt.yticks([])plt.show()
Figure 4: The result of applying a Gaussian filter to a color image
图 4 显示,与均值滤波器相比,高斯滤波器在保留图像边缘方面做得更好,但是它也会在彩色图像上产生伪像。我们现在可以检查高斯滤波器是否会在灰度图像上产生伪像。
new_image_gauss = cv2.GaussianBlur(image2, (figure_size, figure_size),0)plt.figure(figsize=(11,6))plt.subplot(121), plt.imshow(image2, cmap='gray'),plt.title('Original')plt.xticks([]), plt.yticks([])plt.subplot(122), plt.imshow(new_image_gauss, cmap='gray'),plt.title('Gaussian Filter')plt.xticks([]), plt.yticks([])plt.show()
Figure 5: The result of applying a Gaussian filter to a grayscale image
图 5 显示 9 x 9 高斯滤波器应用于灰度图像时不会产生伪像。该滤波器可以保留比 9 x 9 均值滤波器更多的细节,并去除一些噪声。
3.中值滤波器
中值滤波器计算在 n x n 内核中围绕中心像素的像素亮度的中值。然后,中值替换中心像素的像素强度。与均值和高斯滤波器相比,中值滤波器在去除椒盐噪声方面做得更好。中值滤波器保留了图像的边缘,但它不处理斑点噪声。Open-CV 库中的“medianBlur”函数可用于实现中值滤波器。
new_image = cv2.medianBlur(image, figure_size)plt.figure(figsize=(11,6))plt.subplot(121), plt.imshow(cv2.cvtColor(image, cv2.COLOR_HSV2RGB)),plt.title('Original')plt.xticks([]), plt.yticks([])plt.subplot(122), plt.imshow(cv2.cvtColor(new_image, cv2.COLOR_HSV2RGB)),plt.title('Median Filter')plt.xticks([]), plt.yticks([])plt.show()
Figure 6: The result of applying a median filter to a color image.
图 6 显示中值滤波器能够保留图像的边缘,同时消除椒盐噪声。与均值和高斯滤波器不同,中值滤波器不会在彩色图像上产生伪像。中值滤波器现在将应用于灰度图像。
new_image = cv2.medianBlur(image2, figure_size)plt.figure(figsize=(11,6))plt.subplot(121), plt.imshow(image2, cmap='gray'),plt.title('Original')plt.xticks([]), plt.yticks([])plt.subplot(122), plt.imshow(new_image, cmap='gray'),plt.title('Median Filter')plt.xticks([]), plt.yticks([])plt.show()
Figure 7: The result of applying the median filter to a grayscale image
图 7 显示 9 x 9 中值滤波器可以去除一些椒盐噪声,同时保留图像的边缘。
其他过滤器:
以下是几个可用于图像预处理的过滤器:
保守滤波器
保守滤波器用于去除椒盐噪声。确定像素邻域内的最小强度和最大强度。如果中心像素的亮度大于最大值,它将被最大值取代。如果它小于最小值,则用最小值代替。保守滤波器保留边缘,但不去除斑点噪声。
以下代码可用于定义保守过滤器:
# first a conservative filter for grayscale images will be defined.def conservative_smoothing_gray(data, filter_size):temp = []
indexer = filter_size // 2
new_image = data.copy()
nrow, ncol = data.shape
for i in range(nrow):
for j in range(ncol):
for k in range(i-indexer, i+indexer+1):
for m in range(j-indexer, j+indexer+1):
if (k > -1) and (k < nrow):
if (m > -1) and (m < ncol):
temp.append(data[k,m])
temp.remove(data[i,j])
max_value = max(temp)
min_value = min(temp)
if data[i,j] > max_value:
new_image[i,j] = max_value
elif data[i,j] < min_value:
new_image[i,j] = min_value
temp =[]
return new_image.copy()
现在保守滤波器可以应用于灰度图像:
new_image = conservative_smoothing_gray(image2,5)plt.figure(figsize=(11,6))plt.subplot(121), plt.imshow(image2, cmap='gray'),plt.title('Original')plt.xticks([]), plt.yticks([])plt.subplot(122), plt.imshow(new_image, cmap='gray'),plt.title('Conservative Smoothing')plt.xticks([]), plt.yticks([])plt.show()
Figure 9: The result of applying the conservative smoothing filter to a grayscale image
图 9 显示保守平滑滤波器能够消除一些椒盐噪声。它还表明,该滤波器不能像中值滤波器那样去除那么多的椒盐噪声(尽管它确实保留了更多的细节)。)
拉普拉斯滤波器
图像的拉普拉斯算子突出了强度快速变化的区域,因此可以用于边缘检测。如果我们让 I(x,y) 表示图像的强度,则图像的拉普拉斯由以下公式给出:
特定像素处的拉普拉斯算子的离散近似可以通过取该像素的小邻域中的像素强度的加权平均值来确定。图 10 示出了代表近似拉普拉斯算子的两种不同方式的两个核。
Figure 10: Two kernels used to approximate the Laplacian
因为拉普拉斯滤波器检测图像的边缘,所以它可以与高斯滤波器一起使用,以便首先去除斑点噪声,然后突出图像的边缘。这种方法被称为高斯滤波的拉普拉斯算子。Open-CV 库中的“拉普拉斯”函数可用于查找图像的拉普拉斯。
new_image = cv2.Laplacian(image2,cv2.CV_64F)plt.figure(figsize=(11,6))plt.subplot(131), plt.imshow(image2, cmap='gray'),plt.title('Original')plt.xticks([]), plt.yticks([])plt.subplot(132), plt.imshow(new_image, cmap='gray'),plt.title('Laplacian')plt.xticks([]), plt.yticks([])plt.subplot(133), plt.imshow(image2 + new_image, cmap='gray'),plt.title('Resulting image')plt.xticks([]), plt.yticks([])plt.show()
Figure 11: The result of adding the Laplacian of an image to the original image
图 11 示出了虽然将图像的拉普拉斯算子添加到原始图像可以增强边缘,但是一些噪声也被增强。
频率滤波器
当对图像应用频率滤波器时,重要的是首先将图像转换成图像的频域表示。傅立叶变换(将函数分解成正弦和余弦分量)可以应用于图像,以获得其频域表示。我们对图像的频域表示感兴趣的原因是,在频域中对图像应用频率滤波器比在空间域中应用滤波器成本更低。这是因为频域表示中的每个像素对应于图像的频率而不是位置。
低通滤波器和高通滤波器都是频率滤波器。低通滤波器保留最低频率(低于阈值),这意味着它模糊了边缘,并在空间域中从图像中去除了斑点噪声。高通滤波器保留高频,这意味着它保留边缘。“dft”函数确定图像的离散傅立叶变换。对于N×N图像,二维离散傅立叶变换由下式给出:
其中 F 是空间域中的图像值,F 是频域中的图像值。以下是离散傅里叶逆变换(将图像从频域转换到空间域)的公式:
一旦对图像应用了频率滤波器,就可以使用傅立叶逆变换将图像转换回空间域。现在将给出低通滤波器的 python 实现:
dft = cv2.dft(np.float32(image2),flags = cv2.DFT_COMPLEX_OUTPUT)# shift the zero-frequncy component to the center of the spectrum
dft_shift = np.fft.fftshift(dft)# save image of the image in the fourier domain.
magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))# plot both imagesplt.figure(figsize=(11,6))plt.subplot(121),plt.imshow(image2, cmap = 'gray')plt.title('Input Image'), plt.xticks([]), plt.yticks([])plt.subplot(122),plt.imshow(magnitude_spectrum, cmap = 'gray')plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])plt.show()
Figure 12: An image’s spatial domain and frequency domain representations
rows, cols = image2.shapecrow,ccol = rows//2 , cols//2# create a mask first, center square is 1, remaining all zerosmask = np.zeros((rows,cols,2),np.uint8)mask[crow-30:crow+30, ccol-30:ccol+30] = 1# apply mask and inverse DFTfshift = dft_shift*maskf_ishift = np.fft.ifftshift(fshift)img_back = cv2.idft(f_ishift)img_back = cv2.magnitude(img_back[:,:,0],img_back[:,:,1])# plot both imagesplt.figure(figsize=(11,6))plt.subplot(121),plt.imshow(image2, cmap = 'gray')plt.title('Input Image'), plt.xticks([]), plt.yticks([])plt.subplot(122),plt.imshow(img_back, cmap = 'gray')plt.title('Low Pass Filter'), plt.xticks([]), plt.yticks([])plt.show()
Figure 13: The result of applying a low pass filter to an image.
图 13 显示丢失了相当多的细节,但是去除了一些斑点噪声。
Crimmins 斑点去除
Crimmins 互补剔除算法用于去除斑点噪声和平滑边缘。它还降低了椒盐噪声的强度。该算法将图像中一个像素的亮度与其 8 个相邻像素的亮度进行比较。该算法考虑了 4 组邻居(南北、东西、西北-东南、东北-西南。)设 a,b , c 为三个连续像素(例如从 E-S)。那么算法就是:
- 对于每次迭代:
a)暗像素调整:对于四个方向中的每一个方向
1)处理整个图像:如果 a ≥ b + 2 则b=b+1
2)处理整个图像:如果 a > b 和 b ≤ c 则 处理整幅图像用:如果 c > b 和 b ≤ a 则b=b+1
4)处理整幅图像用:如果c≥b+2则 b = b + 处理整幅图像用:如果 a ≤ b — 2 那么b*=b—1
2)处理整幅图像用:如果 a < b 和 b ≥ c 那么b=b 处理整幅图像用:如果 c < b 和 b ≥ a 则b=b—1
4)处理整幅图像用:如果 c ≤ b — 2 则b=b—1***
互补剔除算法的 Python 实现可以在这里找到:https://github . com/m4nv1r/medium _ articles/blob/master/Image _ Filters _ in _ Python . ipynb
图 14 显示了对图像应用 Crimmins 斑点去除滤波器的结果。一些斑点噪声被去除,但是一些边缘是模糊的。
Figure 14: The result of applying the Crimmins Speckle Removal filter
模糊滤镜
钝化滤镜可用于增强图像的边缘。图像过滤器。PIL 软件包的反锐化掩模功能将反锐化滤波器应用于图像(图像首先需要转换为 PIL 图像对象。)图像过滤器。Unsharpmask 函数有三个参数。“半径”参数指定边缘周围有多少相邻像素受到影响。“百分比”参数指定边缘变暗或变亮的程度。第三个参数“阈值”定义了在滤波器执行任何操作之前,相邻色调值之间的距离。
image = Image.fromarray(image.astype('uint8'))
new_image = image.filter(ImageFilter.UnsharpMask(radius=2, percent=150))plt.subplot(121),plt.imshow(image, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(new_image, cmap = 'gray')
plt.title('Unsharp Filter'), plt.xticks([]), plt.yticks([])
plt.show()
Figure 15: The result of applying the Unsharp filter
图 15 显示了反锐化滤波器的结果。当图像的边缘被增强时,一些噪声也被增强。
结论
在去除噪声和保留图像边缘之间总是有一个权衡。为了去除图像中的斑点噪声,需要应用模糊滤波器,这反过来模糊了图像的边缘。如果你想保留图像的边缘,唯一可以去除的噪声就是椒盐噪声。包含本文使用的所有代码的 Jupyter 笔记本可以在这里找到:https://github . com/m4nv1r/medium _ articles/blob/master/Image _ Filters _ in _ python . ipynb