使用 Keras 中的自动编码器进行极端罕见事件分类
在这篇文章中,我们将学习如何实现一个自动编码器来构建一个罕见事件分类器。我们将在这里使用来自的真实世界罕见事件数据集 [1]。
<了解深度学习,了解更多> >
背景
什么是极端罕见事件?
在一个罕见的问题中,我们有一个不平衡的数据集。也就是说,我们的阳性标记样本比阴性标记样本少。在典型的罕见事件问题中,正面标记的数据约占总数的 5-10%。在一个极端罕见的事件问题中,我们只有不到 1%的正面标记数据。例如,在这里使用的数据集中,它约为 0.6%。
这种极端罕见的事件问题在现实世界中很常见,例如,制造过程中的纸张断裂和机器故障,在线行业中的点击或购买。
对这些罕见事件进行分类相当具有挑战性。最近,深度学习已经相当广泛地用于分类。然而,少量的正标签样本阻碍了深度学习应用。无论数据有多大,深度学习的使用都会受到积极标记样本数量的限制。
为什么我们还要费心去使用深度学习呢?
这是一个合理的问题。为什么我们不应该考虑使用另一种机器学习方法呢?
答案是主观的。我们总是可以采用机器学习的方法。为了使其工作,我们可以从负标签数据中进行欠采样,以得到接近平衡的数据集。由于我们有大约 0.6%的正标签数据,欠采样将导致大约是原始数据大小的 1%的数据集。机器学习方法,例如 SVM 或随机森林,仍然可以在这种规模的数据集上工作。然而,它的准确性会有局限性。我们不会利用剩余的大约 99%的数据中的信息。
如果数据足够,深度学习方法可能更有能力。通过使用不同的架构,它还允许模型改进的灵活性。因此,我们将尝试使用深度学习方法。
在这篇文章中,我们将学习如何使用一个简单的密集层自动编码器来构建一个稀有事件分类器。这篇文章的目的是演示极端罕见事件分类的自动编码器的实现。我们将把探索自动编码器的不同架构和配置的任务留给用户。如果你发现什么有趣的东西,请在评论中分享。
分类自动编码器
用于分类的自动编码器方法类似于异常检测。在异常检测中,我们学习正常过程的模式。任何不遵循这种模式的都被归类为异常。对于罕见事件的二进制分类,我们可以使用类似的使用自动编码器的方法(从这里的 [2】)得到)。
快速修订:什么是自动编码器?
- 自动编码器由两个模块组成:编码器和解码器。
- 编码器了解进程的基本特征。这些特征通常尺寸减小。
- 解码器可以从这些底层特征中重建原始数据。
Figure 1. Illustration of an autoencoder. [Source: Autoencoder by Prof. Seungchul Lee
iSystems Design Lab]
如何使用自动编码器稀有事件分类?
- 我们将数据分为两部分:正标和负标。
- 负标记的数据被视为过程的正常状态。正常的状态是当过程无事件时。
- 我们将忽略带正标签的数据,只对带负标签的数据训练自动编码器。
- 该自动编码器现在已经学习了正常过程的特征。
- 一个训练有素的自动编码器将预测来自过程的正常状态的任何新数据(因为它将具有相同的模式或分布)。
- 因此,重建误差会很小。
- 然而,如果我们试图从一个罕见的事件中重建一个数据,自动编码器将会很困难。
- 这将使罕见事件期间的重建误差较高。
- 我们可以捕捉如此高的重建误差,并将其标记为罕见事件预测。
- 该过程类似于异常检测方法。
履行
数据和问题
这是一个来自纸浆造纸厂的二进制标签数据。纸张断裂是造纸过程中的严重问题。一个单独的纸页断裂会造成几千美元的损失,并且工厂每天至少会看到一个或多个断裂。这导致每年数百万美元的损失和工作危害。
由于过程的性质,检测中断事件具有挑战性。如[1]中所述,即使断裂减少 5%,也会给工厂带来巨大的利益。
我们拥有的数据包含 15 天内收集的大约 18k 行。列y
包含二进制标签,1 表示分页符。其余列是预测值。大约有 124 个阳性标记样品(~0.6%)。
在这里下载数据。
代码
导入所需的库。
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as snsimport pandas as pd
import numpy as np
from pylab import rcParamsimport tensorflow as tf
from keras.models import Model, load_model
from keras.layers import Input, Dense
from keras.callbacks import ModelCheckpoint, TensorBoard
from keras import regularizersfrom sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, precision_recall_curve
from sklearn.metrics import recall_score, classification_report, auc, roc_curve
from sklearn.metrics import precision_recall_fscore_support, f1_scorefrom numpy.random import seed
seed(1)
from tensorflow import set_random_seed
set_random_seed(2)SEED = 123 #used to help randomly select the data points
DATA_SPLIT_PCT = 0.2rcParams['figure.figsize'] = 8, 6
LABELS = ["Normal","Break"]
请注意,我们设置随机种子是为了结果的可重复性。
数据预处理
现在,我们阅读和准备数据。
df = pd.read_csv("data/processminer-rare-event-mts - data.csv")
这个罕见事件问题的目的是在板块断裂发生之前预测它。我们会尽量提前 4 分钟预测课间休息。为了构建这个模型,我们将把标签上移 2 行(对应于 4 分钟)。我们可以这样做df.y=df.y.shift(-2)
。然而,在这个问题中,我们希望进行这样的移位:如果第 n 行被正标记,
- 使 row ( n -2)和( n -1)等于 1。这将帮助分类器学习到 4 分钟前的预测。
- 删除第行第行。因为我们不希望分类器学习预测已经发生的中断。
我们将为这种曲线移动开发以下 UDF。
sign = **lambda** x: (1, -1)[x < 0]
**def** curve_shift(df, shift_by):
*'''*
*This function will shift the binary labels in a dataframe.*
*The curve shift will be with respect to the 1s.*
*For example, if shift is -2, the following process*
*will happen: if row n is labeled as 1, then*
*- Make row (n+shift_by):(n+shift_by-1) = 1.*
*- Remove row n.*
*i.e. the labels will be shifted up to 2 rows up.*
*Inputs:*
*df A pandas dataframe with a binary labeled column.*
*This labeled column should be named as 'y'.*
*shift_by An integer denoting the number of rows to shift.*
*Output*
*df A dataframe with the binary labels shifted by shift.*
*'''*
vector = df['y'].copy()
**for** s **in** range(abs(shift_by)):
tmp = vector.shift(sign(shift_by))
tmp = tmp.fillna(0)
vector += tmp
labelcol = 'y'
*# Add vector to the df*
df.insert(loc=0, column=labelcol+'tmp', value=vector)
*# Remove the rows with labelcol == 1.*
df = df.drop(df[df[labelcol] == 1].index)
*# Drop labelcol and rename the tmp col as labelcol*
df = df.drop(labelcol, axis=1)
df = df.rename(columns={labelcol+'tmp': labelcol})
*# Make the labelcol binary*
df.loc[df[labelcol] > 0, labelcol] = 1
**return** df
在继续之前,为了简单起见,我们将删除时间和分类列。
*# Remove time column, and the categorical columns*
df = df.drop(['time', 'x28', 'x61'], axis=1)
现在,我们将数据分为训练集、有效集和测试集。然后,我们将采用只有 0 的数据子集来训练自动编码器。
df_train, df_test = train_test_split(df, test_size=DATA_SPLIT_PCT, random_state=SEED)
df_train, df_valid = train_test_split(df_train, test_size=DATA_SPLIT_PCT, random_state=SEED)df_train_0 = df_train.loc[df['y'] == 0]
df_train_1 = df_train.loc[df['y'] == 1]
df_train_0_x = df_train_0.drop(['y'], axis=1)
df_train_1_x = df_train_1.drop(['y'], axis=1)df_valid_0 = df_valid.loc[df['y'] == 0]
df_valid_1 = df_valid.loc[df['y'] == 1]
df_valid_0_x = df_valid_0.drop(['y'], axis=1)
df_valid_1_x = df_valid_1.drop(['y'], axis=1)df_test_0 = df_test.loc[df['y'] == 0]
df_test_1 = df_test.loc[df['y'] == 1]
df_test_0_x = df_test_0.drop(['y'], axis=1)
df_test_1_x = df_test_1.drop(['y'], axis=1)
标准化
对于自动编码器,通常最好使用标准化数据(转换为高斯数据,均值为 0,方差为 1)。
scaler = StandardScaler().fit(df_train_0_x)
df_train_0_x_rescaled = scaler.transform(df_train_0_x)
df_valid_0_x_rescaled = scaler.transform(df_valid_0_x)
df_valid_x_rescaled = scaler.transform(df_valid.drop(['y'], axis = 1))df_test_0_x_rescaled = scaler.transform(df_test_0_x)
df_test_x_rescaled = scaler.transform(df_test.drop(['y'], axis = 1))
自动编码器分类器
初始化
首先,我们将初始化自动编码器架构。我们正在构建一个简单的自动编码器。应该探索更复杂的架构和其他配置。
nb_epoch = 200
batch_size = 128
input_dim = df_train_0_x_rescaled.shape[1] *#num of predictor variables,*
encoding_dim = 32
hidden_dim = int(encoding_dim / 2)
learning_rate = 1e-3
input_layer = Input(shape=(input_dim, ))
encoder = Dense(encoding_dim, activation="relu", activity_regularizer=regularizers.l1(learning_rate))(input_layer)
encoder = Dense(hidden_dim, activation="relu")(encoder)
decoder = Dense(hidden_dim, activation="relu")(encoder)
decoder = Dense(encoding_dim, activation="relu")(decoder)
decoder = Dense(input_dim, activation="linear")(decoder)
autoencoder = Model(inputs=input_layer, outputs=decoder)
autoencoder.summary()
培训
我们将训练模型并将其保存在文件中。保存训练好的模型是为将来的分析节省时间的好方法。
autoencoder.compile(metrics=['accuracy'],
loss='mean_squared_error',
optimizer='adam')cp = ModelCheckpoint(filepath="autoencoder_classifier.h5",
save_best_only=True,
verbose=0)tb = TensorBoard(log_dir='./logs',
histogram_freq=0,
write_graph=True,
write_images=True)history = autoencoder.fit(df_train_0_x_rescaled, df_train_0_x_rescaled,
epochs=nb_epoch,
batch_size=batch_size,
shuffle=True,
validation_data=(df_valid_0_x_rescaled, df_valid_0_x_rescaled),
verbose=1,
callbacks=[cp, tb]).history
Figure 2. Loss for Autoencoder Training.
分类
在下文中,我们展示了如何将自动编码器重构误差用于罕见事件分类。
如前所述,如果重建误差较高,我们会将其归类为断纸。我们需要确定这方面的门槛。
我们将使用验证集来确定阈值。
valid_x_predictions = autoencoder.predict(df_valid_x_rescaled)
mse = np.mean(np.power(df_valid_x_rescaled - valid_x_predictions, 2), axis=1)
error_df = pd.DataFrame({'Reconstruction_error': mse,
'True_class': df_valid['y']})precision_rt, recall_rt, threshold_rt = precision_recall_curve(error_df.True_class, error_df.Reconstruction_error)
plt.plot(threshold_rt, precision_rt[1:], label="Precision",linewidth=5)
plt.plot(threshold_rt, recall_rt[1:], label="Recall",linewidth=5)
plt.title('Precision and recall for different threshold values')
plt.xlabel('Threshold')
plt.ylabel('Precision/Recall')
plt.legend()
plt.show()
Figure 3. A threshold of 0.4 should provide a reasonable trade-off between precision and recall.
现在,我们将对测试数据进行分类。
我们不应该从测试数据中估计分类阈值。这将导致过度拟合。
test_x_predictions = autoencoder.predict(df_test_x_rescaled)
mse = np.mean(np.power(df_test_x_rescaled - test_x_predictions, 2), axis=1)
error_df_test = pd.DataFrame({'Reconstruction_error': mse,
'True_class': df_test['y']})
error_df_test = error_df_test.reset_index()threshold_fixed = 0.4
groups = error_df_test.groupby('True_class')fig, ax = plt.subplots()for name, group in groups:
ax.plot(group.index, group.Reconstruction_error, marker='o', ms=3.5, linestyle='',
label= "Break" if name == 1 else "Normal")
ax.hlines(threshold_fixed, ax.get_xlim()[0], ax.get_xlim()[1], colors="r", zorder=100, label='Threshold')
ax.legend()
plt.title("Reconstruction error for different classes")
plt.ylabel("Reconstruction error")
plt.xlabel("Data point index")
plt.show();
Figure 4. Using threshold = 0.4 for classification. The orange and blue dots above the threshold line represents the True Positive and False Positive, respectively.
在图 4 中,阈值线上方的橙色和蓝色圆点分别代表真阳性和假阳性。如我们所见,我们有很多误报。为了看得更清楚,我们可以看到一个混淆矩阵。
pred_y = [1 if e > threshold_fixed else 0 for e in error_df.Reconstruction_error.values]conf_matrix = confusion_matrix(error_df.True_class, pred_y)plt.figure(figsize=(12, 12))
sns.heatmap(conf_matrix, xticklabels=LABELS, yticklabels=LABELS, annot=True, fmt="d");
plt.title("Confusion matrix")
plt.ylabel('True class')
plt.xlabel('Predicted class')
plt.show()
Figure 5. Confusion Matrix on the test predictions.
我们可以预测 41 个中断实例中的 8 个。请注意,这些情况包括提前 2 或 4 分钟的预测。这在 20%左右,对于造纸行业来说是一个不错的召回率。假阳性率在 6%左右。对于一个磨坊来说,这并不理想,但也不可怕。
尽管如此,该模型还可以进一步改进,以较小的误报率提高召回率。我们将查看下面的 AUC,然后讨论下一步的改进方法。
ROC 曲线和 AUC
false_pos_rate, true_pos_rate, thresholds = roc_curve(error_df.True_class, error_df.Reconstruction_error)
roc_auc = auc(false_pos_rate, true_pos_rate,)plt.plot(false_pos_rate, true_pos_rate, linewidth=5, label='AUC = %0.3f'% roc_auc)
plt.plot([0,1],[0,1], linewidth=5)plt.xlim([-0.01, 1])
plt.ylim([0, 1.01])
plt.legend(loc='lower right')
plt.title('Receiver operating characteristic curve (ROC)')
plt.ylabel('True Positive Rate')
plt.xlabel('False Positive Rate')
plt.show()
AUC 是 0.69。
Github 库
带注释的全部代码都呈现在这里。
稀有事件分类的自动编码器模型。通过创建……为 cran 2367/auto encoder _ classifier 开发做出贡献
github.com](https://github.com/cran2367/autoencoder_classifier/blob/master/autoencoder_classifier.ipynb)
这里有哪些地方可以做得更好?
自动编码器优化
自动编码器是 PCA 的非线性扩展。然而,上面开发的传统自动编码器不遵循 PCA 的原理。在构建正确的自动编码器——使用 PCA 原理进行调整和优化。第一部分和第二部分,解释并实现了自动编码器中应包含的 PCA 优化原则。
LSTM 自动编码器
这里讨论的问题是一个(多元)时间序列。然而,在自动编码器模型中,我们没有考虑时间信息/模式。在下一篇文章中,我们将探索 RNN 是否可行。我们将尝试一个 LSTM 自动编码器。
结论
我们研究了来自造纸厂的极端罕见事件二进制标记数据,以构建自动编码器分类器。我们达到了合理的精确度。这里的目的是演示如何使用基本的自动编码器进行罕见事件分类。我们将进一步开发其他方法,包括一个 LSTM 自动编码器,它可以提取时间特征以获得更好的准确性。
关于 LSTM 自动编码器的下一个帖子在这里, LSTM 自动编码器罕见事件分类。
推荐后续阅读
参考
- Ranjan、m . Mustonen、k . pay nabar 和 k . pour AK(2018 年)。数据集:多元时间序列中的稀有事件分类。T3【arXiv 预印本 arXiv:1809.10717。
- https://www . data science . com/blog/fraud-detection-with-tensor flow
- Github 回购:https://github.com/cran2367/autoencoder_classifier
免责声明:这篇文章的范围仅限于构建密集层自动编码器并将其用作罕见事件分类器的教程。通过网络调优,从业者有望获得更好的结果。这篇文章的目的是帮助数据科学家实现一个自动编码器。
极度不平衡的数据—欺诈检测
欠采样+逻辑回归
为了打击欺诈,我们必须首先发现它。发现欺诈时,您必须考虑:
- 如果你试图找到所有的欺诈案例,其中一些案例将会被误标。这将导致无辜的人被指控犯有欺诈罪。
- 如果你试图让无辜的人免于被指控,你会给一些诈骗犯贴上无辜的标签。在这种情况下,你的公司会损失更多的钱。
你的欺诈检测算法不完美是必然的。你会倾向哪边?
再来看这个数据。
这是一个货币交易的数据集。它给出了发送者的 ID、接收者的 ID、被转移的金额以及交易前后发送者和接收者的余额。它还告诉我们哪些样本是欺诈,哪些不是。它是一个生成的数据集。公司不想让你知道他们损失了多少钱,所以这是我们唯一能做的。
让我们加载数据集,看看它是什么样子:
cols = ['step', 'type', 'amount', 'nameOrig', 'oldbalanceOrg', 'newbalanceOrig',
'nameDest', 'oldbalanceDest', 'newbalanceDest', 'isFraud', 'isFlaggedFraud']
df = pd.read_csv('PS_20174392719_1491204439457_log.csv', header = 0, names = cols)
print('df.shape:', df.shape)
df.head()
我们可以把桌子水平分成不同的组。每个集合都有所有的特征,但不是所有的观察值。
对于训练集,我们可以使用 isFraud 列值来训练我们的模型。对测试集应用该模型将为我们提供每次观察的预测 isFraud 值。
一个简单的方法是尝试:
pd.value_counts(df.isFraud, normalize = True)
另一种方法是使用 isFraud 列的模式:
majority_class = df.isFraud.mode()[0]
y_pred = np.full(shape = df.isFraud.shape, fill_value = majority_class)
accuracy_score(df.isFraud, y_pred)
这给了我们一个 0.998709 的准确度分值,与 value_counts()相同——这是预期的。与 isFraud=1 相比,isFraud=0 有更多的值。我们可以从上面的 value_counts()或下面打印的分类报告中看到这一点:
print(classification_report(df.isFraud, y_pred))
我们可以看到,我们得到了完美的召回和精度,但支持值告诉我们另一个故事。我们有 6354407 个值支持 isFraud=0 的情况,有 8213 个值支持 isFraud=1。
不求准确性,就求 ROC AUC 分吧。这个分数衡量我们的模型区分类别的能力。
roc_auc_score(df.isFraud, y_pred)
这使我们的值为 0.5。ROC AUC 得分的值 1.0 是任何人在任何模型中所能得到的最好值。为什么我们得到了 0.5?这是因为我们可以完美地预测所有 isFraud = 0 的情况,但没有一个 isFraud = 1 的情况。因此,在这两个类别中,我们只能预测 1(这使我们的 ROC AUC 为 0.5)。
为了让我们的模型公平竞争,我们可以对欺诈交易进行过采样,或者对干净交易进行欠采样。我们可以通过使用不平衡学习库来实现这一点。
from imblearn.under_sampling import RandomUnderSamplerX = df.drop(['isFraud', 'type', 'nameOrig', 'nameDest'], axis = 1)
y = df.isFraud
rus = RandomUnderSampler(sampling_strategy=0.8)
X_res, y_res = rus.fit_resample(X, y)
print(X_res.shape, y_res.shape)
print(pd.value_counts(y_res))
RandomUnderSampler 的 sampling_strategy 设置为 0.8。这只是为了展示当我们这样做时会发生什么。它允许我们指定少数类样本与多数类样本的比率。它为我们提供了 18479 行数据,其值计数如下:
让我们看看在重采样并删除这些列后,我们的表是什么样子的:
cols_numeric = ['step', 'amount', 'oldbalanceOrg', 'newbalanceOrig',
'oldbalanceDest', 'newbalanceDest', 'isFlaggedFraud']
df_rus = pd.DataFrame(X_res, columns = cols_numeric)
df_rus.head()
现在,让我们将数据集分成 3 个部分—训练、验证和测试数据集。我们可以在不同的模型中反复使用验证数据集。一旦我们认为我们得到了最好的模型,我们将使用我们的测试数据集。
我们这样做的原因是,我们的模型不仅应该使用部分训练数据集为我们提供良好的结果,还应该使用我们从未见过的数据提供良好的结果。这就是现实生活中会发生的事情。通过保留只使用一次的测试数据集,我们强迫自己不要过度适应验证数据集。这也是 Kaggle 竞赛所做的。
trainsize/valsize/testsize 显示了应该为训练/验证/测试保留的总数据集的一部分。
from sklearn.model_selection import train_test_splitdef train_validation_test_split(
X, y, train_size=0.8, val_size=0.1, test_size=0.1,
random_state=None, shuffle=True): assert int(train_size + val_size + test_size + 1e-7) == 1 X_train_val, X_test, y_train_val, y_test = train_test_split(
X, y, test_size=test_size, random_state=random_state, shuffle=shuffle) X_train, X_val, y_train, y_val = train_test_split(
X_train_val, y_train_val, test_size=val_size/(train_size+val_size),
random_state=random_state, shuffle=shuffle) return X_train, X_val, X_test, y_train, y_val, y_test X_train, X_val, X_test, y_train, y_val, y_test = train_validation_test_split(
X_res, y_res, train_size=0.8, val_size=0.1, test_size=0.1, random_state=1)
class_weight = {0: 4, 1: 5}
model = LogisticRegression(class_weight=class_weight)
model.fit(X_train, y_train)y_pred = model.predict(X_val)
print(classification_report(y_val, y_pred))
print('accuracy', accuracy_score(y_val, y_pred))
roc_auc_score(y_val, y_pred)
请注意 class_weight 参数。我们把它放在那里是因为欠采样行数在 isFraud=0 时是 10000,在 isFraud=1 时是 8000。我们要称它们的重量,使它们保持平衡。这样做的比例是 4:5,这是这里使用的类权重。
如果我们在 sampling_strategy=0.8 的情况下进行了欠采样,我们将拥有平衡的类,并且不需要 class_weight 参数。如果我们得到一个新的数据集,它有轻微的不平衡参数,我们可以使用带有类权重的 LogisticRegression 来平衡它,而不需要重采样。
现在我们得到了 0.90 的准确度分数,这是一个很好的分数。我们的 ROC AUC 得分也是 0.9。
现在,让我们在测试数据集上尝试我们的模型:
y_pred = model.predict(X_test)
print(classification_report(y_test, y_pred))
print('Accuracy', accuracy_score(y_test, y_pred))
print('ROC AUC score:', roc_auc_score(y_test, y_pred))
又是 0.90 的好成绩。看来 RandomUnderSampler 干得不错。
我们必须将我们的模型应用于完整的(未采样的)数据集。让我们接下来做那件事。
y_pred = model.predict(X)
print(classification_report(y, y_pred))
print('Accuracy:', accuracy_score(y, y_pred))
print('ROC AUC score:', roc_auc_score(y, y_pred))
这个更有意思。准确性和 ROC AUC 得分非常好,isFraud=0 的精确度/召回率/f1 得分也非常好。问题是 isFraud=1 的精度非常非常低,只有 0.01。因为 f1 分数是精确度和召回率的加权平均值,所以它也很低,为 0.02。也许我们没有足够的数据来做好逻辑回归。或者我们应该过采样而不是欠采样。
我们将继续使用这个数据集,在以后的博客文章中应用许多技术。非常感谢 Lambda 学校的讲师 Ryan Herr 提供了 train_validation_test_split()函数。
数据科学的寓言——安斯科姆的四重奏
寓言是一个简短的故事,它可以给人一个教训或者传达一个寓意。在这里,我们探索安斯库姆的四重奏,看看它警告我们什么样的恐怖。
从前…
…在一个很远很远的地方,曾经住着一个叫弗朗西斯·约翰“弗兰克”安斯科姆的人。他是一位非常有名的统计学家。
他四处游荡,会见了许多同行,并坐在城堡里训练年轻的学徒学习神圣的统计学艺术。他对人们说得越多,训练得越多,他就看到了一种危险的趋势。**人们倾向于忽视可视化,而偏爱汇总统计。**大家都说绘制数据太费劲了。
A worried Frank is a Red Frank
这很危险,弗兰克非常担心。这场瘟疫传播得很快,他没有时间去对抗它。他必须做点什么,做点什么来阻止这一切。
所以他跑去和长老议会谈话,但是每个人都嘲笑他。
“当你有了数据的意思,
你还想考察什么?
标准差只是崇高,
绘图是浪费时间!”PoorPoetrix 大师
弗兰克从来没有预料到议会也遭到了毒害,他不得不睁开眼睛。
所以他旅行了,向北旅行到永远是冬天的地方,到达霜封山的顶峰。他坐在那里,陷入沉思。5 分钟后,他意识到外面的山里冷得要命,他应该多带些羊毛衣服。
他冷得直哆嗦,四处寻找一个洞穴安顿下来,幸运的是他找到了一个。
弗兰克在那里冥想,他深度调解,尽管有人声称他只是睡着了。
在他的梦里,他看到了数字,数字和更多的数字,就在那时,他意识到数字是解决问题的关键。他日复一日地琢磨这些数字,终于解开了它们的秘密。
他叫来一只鸟,并通过他送给委员会一张羊皮纸,上面有 4 组 11 个数据点,并请求委员会按照他的遗愿来绘制这些点:
.
.
+-------+--------+-------+-------+-------+-------+-------+------+
| ** I | II | III | IV ** |
+-------+--------+-------+-------+-------+-------+-------+------+
| x | y | x | y | x | y | x | y | +-------+--------+-------+-------+-------+-------+-------+------+
| 10.0 | 8.04 | 10.0 | 9.14 | 10.0 | 7.46 | 8.0 | 6.58 |
| 8.0 | 6.95 | 8.0 | 8.14 | 8.0 | 6.77 | 8.0 | 5.76 |
| 13.0 | 7.58 | 13.0 | 8.74 | 13.0 | 12.74 | 8.0 | 7.71 |
| 9.0 | 8.81 | 9.0 | 8.77 | 9.0 | 7.11 | 8.0 | 8.84 |
| 11.0 | 8.33 | 11.0 | 9.26 | 11.0 | 7.81 | 8.0 | 8.47 |
| 14.0 | 9.96 | 14.0 | 8.10 | 14.0 | 8.84 | 8.0 | 7.04 |
| 6.0 | 7.24 | 6.0 | 6.13 | 6.0 | 6.08 | 8.0 | 5.25 |
| 4.0 | 4.26 | 4.0 | 3.10 | 4.0 | 5.39 | 19.0 |12.50 |
| 12.0 | 10.84 | 12.0 | 9.13 | 12.0 | 8.15 | 8.0 | 5.56 |
| 7.0 | 4.82 | 7.0 | 7.26 | 7.0 | 6.42 | 8.0 | 7.91 |
| 5.0 | 5.68 | 5.0 | 4.74 | 5.0 | 5.73 | 8.0 | 6.89 |
+-------+--------+-------+-------+-------+-------+-------+------+
按照他们的习惯,委员会只用描述性统计数据对他们进行分析,他们得到了完全相同的结果,他们想知道老傻瓜弗兰克又在搞什么名堂:
**Summary**
+-----+---------+-------+---------+-------+----------+
| Set | mean(X) | sd(X) | mean(Y) | sd(Y) | cor(X,Y) |
+-----+---------+-------+---------+-------+----------+
| 1 | 9 | 3.32 | 7.5 | 2.03 | 0.816 |
| 2 | 9 | 3.32 | 7.5 | 2.03 | 0.816 |
| 3 | 9 | 3.32 | 7.5 | 2.03 | 0.816 |
| 4 | 9 | 3.32 | 7.5 | 2.03 | 0.817 |
+-----+---------+-------+---------+-------+----------+
但是 PoorPoetrix 大师决定尊重他朋友的请求,就在那时,当他绘制数据集时,奇迹出现了:
Anscombe’s quartet — Plot
他发现图表完全不同,尽管摘要完全相似。
- 第一个散点图(左上)似乎是一个简单的线性关系,对应于两个相关变量,并遵循正态假设。
- 第二张图(右上)不是正态分布的;虽然这两个变量之间的关系很明显,但它不是线性的,皮尔逊相关系数也不相关。更一般的回归和相应的决定系数会更合适。
- 在第三张图中(左下方),分布是线性的,但应该有不同的回归线(稳健回归将被要求)。计算的回归被一个异常值抵消,该异常值施加足够的影响以将相关系数从 1 降低到 0.816。
- 最后,第四个图(右下)显示了一个例子,其中一个高杠杆点足以产生高相关系数,即使其他数据点并不表明变量之间的任何关系。
PoorPoetrix 大师意识到了委员会的愚蠢并改正了它们,这个数据集被称为 Anscombe 的四重奏。
弗兰克拯救了世界,作为一个快乐的老人在山洞里度过了他的最后几天。
.
.
这个故事的寓意
人们普遍引用 PoorPoetrix 大师用这些不朽的话总结了这一事件:
为了得到你可以信任的分析,
绘制你的数据,你必须绘制。PoorPoetrix 大师
参考
- https://en.wikipedia.org/wiki/Anscombe%27s_quartet
- https://www . r-bloggers . com/using-and-abuse-data-visualization-anscombes-quartet-and-checking-bonferroni/
- gif 图片取自https://giphy.com/
人脸对齐:深度多任务学习
1。简介
面部关键点预测:给定一张面部图片,预测各种面部特征的位置。
这个问题属于计算机视觉的范畴,并伴随着它自身的挑战。
在这方面已经做了很多工作。在本文中,我们将深入研究这篇 论文 提出的独特方法,并从头实现它,全部使用 keras。
Deep Multi-task learning
2。为什么我们需要解决它?它的应用是什么?
你有没有用过 Snapchat,专门试用过他们的图像滤镜?它是如何变得如此神奇的?它是如何如此准确地用假胡子代替你的胡子的?
要做到这一点,首先它需要识别你脸上与胡子相对应的部分。然后切掉一部分(当然是内部的)并用人造的替换掉。
这就是面部关键点检测发挥作用的地方。识别面部的不同部分。
这只是一个具体的应用,还有很多这样的应用。查看这篇文章了解更多信息。
3.数据概述
这个项目的数据集由论文作者自己提供,可以在 这里 找到。
数据总共有 12295 幅图像,其中 10000 幅是训练图像,2295 幅是测试图像。
数据还附带了两个 txt 文件:training.txt
和testing.txt
。这两个文件保存了关于图像路径、面部特征的坐标位置和 4 个其他面部属性的信息:
第一属性:性别【男/女】
第二属性:微笑/不微笑
第三属性:戴眼镜/不戴眼镜
第四属性:姿态变化
3.1 加载和清理数据
让我们加载 training.txt 文件,并尝试理解和分析数据。当你使用pandas read_csv
函数读取 training.txt 文件时,使用空格作为分隔符,它不会被正确加载,这是因为每行的开头都有空格。所以,我们需要把它去掉。
Training.txt file
下面的代码将完全做到这一点。
f = open('training.txt','r')
f2 = open('training_new.txt','w')
for i,line in enumerate(f.readlines()):
if i==0:
continue
line = line.strip()
f2.write(line)
f2.write('\n')
f2.close()
f.close()
现在,我们将在项目中使用这个新创建的文件training_new.txt
。对于testing.txt
文件也是如此。
读取已清理的training.txt
文件。
names = ['Path']+list('BCDEFGHIJK')+['Gender','Smile','Glasses','Pose']train = pd.read_csv('training_new.txt',sep=' ',header=None,names=names) train['Path'] = train['Path'].str.replace('\\','/')
下面是培训文件中每个属性的含义。
- 路径:图像的路径(绝对路径)
- b:右眼中心的 x 坐标
- c:左眼中心的 x 坐标
- 鼻中心的 D: x 坐标
- e:嘴部最右侧点的 x 坐标
- f:嘴部最左侧点的 x 坐标
- 右眼中心的 G: y 坐标
- 左眼中心的 H: y 坐标
- 鼻中心的 I: y 坐标
- 嘴部最右端点的 y 坐标
- 嘴巴最左边的 y 坐标
- 性别:此人是男是女,1:男,2:女
- 微笑:不管对方是否微笑,1:微笑,2:不微笑
- 眼镜:这个人是否戴眼镜,1:戴眼镜,2:不戴眼镜
- 姿势:【姿势估计】,5 类。
3.2 可视化数据
现在,让我们想象一些带有面部关键点的图像。
代码:
#visualising the dataset
images = []
all_x = []
all_y= []
random_ints = np.random.randint(low=1,high=8000,size=(9,))
for i in random_ints:
img = cv2.imread(train['Path'].iloc[i])
x_pts = train[list('BCDEF')].iloc[i].values.tolist()
y_pts = train[list('GHIJK')].iloc[i].values.tolist()
all_x.append(x_pts)
all_y.append(y_pts)
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
images.append(img)fig,axs = plt.subplots(nrows=3,ncols=3,figsize=(14,10))k =0
for i in range(0,3):
for j in range(0,3):
axs[i,j].imshow(images[k])
axs[i,j].scatter(all_x[k],all_y[k])
k += 1plt.show()
4。深潜
现在,我们知道面部关键点预测是怎么回事了。让我们深入了解一下它的技术细节。
接受图像作为输入,并给出面部特征的坐标。
这是一个回归问题,因为它预测连续值,即面部标志的坐标。
Face Alignment, Magic box?
盒子里有什么神奇的东西能做这些?
让我们更深入地了解它。
到目前为止,有两种方法可以解决这个问题,一种是普通的计算机视觉技术(如 viola 和 jones 的人脸包围盒预测),另一种是基于深度学习的,特别是基于卷积神经网络的。
但是这个卷积神经网络到底是个什么鬼?
简单地说,它是用来从图像中提取和检测有意义信息的技术。如果你有兴趣了解更多,请点击这里的。
对于这个问题,我们将采取第二条路线,即基于深度学习。
5.文献综述
不同的研究人员在这个领域做了大量的工作。围绕这个问题的大部分工作把它作为一个单一的任务问题,他们试图单独解决这个问题。但是 这篇研究论文 提出了一个有趣的想法,那就是,他们把它作为一个深层的多任务问题。
什么是多任务问题?
多任务问题:与其只解决一个主要问题,不如一起解决相关的多个问题。
不要只解决面部标志检测问题,让我们也解决相关的辅助问题,比如:图像中的人是否相似,这个人的性别,等等…
但是为什么要一起解决多个任务呢?
上述论文的作者注意到关于面部标志检测(主要任务)的一个非常关键的细节,即面部标志的位置高度依赖于人是否在微笑、图像中人的姿势以及其他支持的任务。因此,他们引入了深度多任务学习的概念,并发现它对于这个问题是准确的。
6.履行
如果你已经熟悉深度学习,到现在,你应该知道这是一个多输出问题,因为我们试图同时解决这个多任务。由于我们将使用 keras 来实现,所以多输出模型可以通过功能 API 来实现,而不是顺序 API。****
根据数据,我们手头有 5 个任务,其中面部对齐是主要的一个。因此,我们将使用多输出模型来训练这 5 项任务的模型。
我们将用不同的辅助任务来训练主任务(人脸对齐),以评估深度多任务学习的有效性。
第一个模型:面部对齐+所有其他辅助任务(4)
第二种模式:脸部对齐+性别+微笑+眼镜
第三种模型:人脸对齐+姿态估计
第四种模式:仅面部对齐
网络架构
我们将使用四个卷积层、三个最大池层、一个密集层,以及用于所有任务的独立输出层。除了图像的输入形状之外,网络架构与本文作者实现的网络架构相同。
Network architecture. Source
6.1 第一个模型的实施
代码如下:
inp = Input(shape=(160,160,3))#1st convolution pair
conv1 = Conv2D(16,kernel_size=(5,5), activation='relu')(inp)
mx1 = MaxPooling2D(pool_size=(2,2))(conv1)#2nd convolution pair
conv2 = Conv2D(48,kernel_size=(3,3), activation='relu')(mx1)
mx2 = MaxPooling2D(pool_size=(2,2))(conv2)#3rd convolution pair
conv3 = Conv2D(64,kernel_size=(3,3), activation='relu')(mx2)
mx3 = MaxPooling2D(pool_size=(2,2))(conv3)#4th convolution pair
conv4 = Conv2D(64,kernel_size=(2,2), activation='relu')(mx3)flt = Flatten()(conv4)dense = Dense(100,activation='relu')(flt)reg_op = Dense(10,activation='linear',name='key_point')(dense)gndr_op = Dense(2,activation='sigmoid',name='gender')(dense)smile_op = Dense(2,activation='sigmoid',name='smile')(dense)glasses_op = Dense(2,activation='sigmoid',name='glasses')(dense)pose_op = Dense(5,activation='softmax',name='pose')(dense)model = Model(inp,[reg_op,gndr_op,smile_op,glasses_op,pose_op])model.summary()
这将打印出以下输出:
现在,下一步是提及我们将在 keras 中为每个输出使用的损失函数。这很容易弄清楚。我们将对面部关键点使用均方误差(MSE)** ,对性别输出、微笑输出和眼镜输出使用二元交叉熵,对姿势输出使用分类交叉熵。**
loss_dic = {'key_point':'mse','gender':'binary_crossentropy','smile':'binary_crossentropy', 'glasses':'binary_crossentropy' , 'pose':'categorical_crossentropy'}
在内部,总损失将是所有单个损失的总和。现在,我们也可以在 keras 中为每个损失函数显式设置权重,得到的损失将是所有单个损失的加权和。
由于主要任务是关键点检测,因此我们将给予更多的权重。
loss_weights = {'key_point':7,'gender':2,'smile':4,'glasses':1,'pose':3}
每项任务的衡量标准是:
metrics = {'key_point':'mse','gender':['binary_crossentropy','acc'],'smile':['binary_crossentropy','acc'], 'glasses':['binary_crossentropy','acc'] , 'pose':['categorical_crossentropy','acc']}
一切就绪。让我们训练网络。
epochs = 35
bs = 64H = model.fit(train_images,[train_keypoint_op,]+train_categorical_ops, epochs = epochs, batch_size=bs, validation_data=(val_images,[val_keypoint_op,]+val_categorical_ops),callbacks=[TrainValTensorBoard(log_dir='./log',write_graph=False)])
让我们评价一下 model 的性能。
train_pred = model.predict(train_images)
val_pred = model.predict(val_images)print('MSE on train data: ', mean_squared_error(train_keypoint_op,train_pred[0]))
print('MSE on validation data: ', mean_squared_error(val_keypoint_op,val_pred[0]))
上面的代码片段给出了以下输出:
MSE on train data: 2.0609966325423565
MSE on validation data: 29.55315040683187
可视化验证集上的结果。
Output Result: Model 1
它仅用 35 个纪元和相当简单的模型架构就能很好地工作。
6.2 第二种模式的实施
这一个的代码如下:
inp = Input(shape=(160,160,3))#1st convolution pair
conv1 = Conv2D(16,kernel_size=(5,5), activation='relu')(inp)
mx1 = MaxPooling2D(pool_size=(2,2))(conv1)#2nd convolution pair
conv2 = Conv2D(48,kernel_size=(3,3), activation='relu')(mx1)
mx2 = MaxPooling2D(pool_size=(2,2))(conv2)#3rd convolution pair
conv3 = Conv2D(64,kernel_size=(3,3), activation='relu')(mx2)
mx3 = MaxPooling2D(pool_size=(2,2))(conv3)#4th convolution pair
conv4 = Conv2D(64,kernel_size=(2,2), activation='relu')(mx3)flt = Flatten()(conv4)dense = Dense(100,activation='relu')(flt)reg_op = Dense(10,activation='linear',name='key_point')(dense)gndr_op = Dense(2,activation='sigmoid',name='gender')(dense)smile_op = Dense(2,activation='sigmoid',name='smile')(dense)glasses_op = Dense(2,activation='sigmoid',name='glasses')(dense)model = Model(inp,[reg_op,gndr_op,smile_op,glasses_op])model.summary()
编译模型。
loss_dic ={'key_point':'mse','gender':'binary_crossentropy','smile':'binary_crossentropy', 'glasses':'binary_crossentropy' }loss_weights = {'key_point':2,'gender':1,'smile':4,'glasses':1}metrics = {'key_point':'mse','gender':['binary_crossentropy','acc'],'smile':['binary_crossentropy','acc'], 'glasses':['binary_crossentropy','acc'] }model.compile(optimizer='adam',loss=loss_dic,loss_weights=loss_weights,metrics=metrics)
一切就绪。让我们训练网络。
H = model.fit(train_images, [train_keypoint_op,]+train_categorical_ops[:-1], epochs = epochs, batch_size=bs, validation_data=(val_images,[val_keypoint_op,]+val_categorical_ops[:-1]),callbacks=[TrainValTensorBoard(log_dir='./log3',write_graph=False)])
让我们评价一下 model 的性能。
train_pred = model.predict(train_images)
val_pred = model.predict(val_images)print('MSE on train data: ', mean_squared_error(train_keypoint_op,train_pred[0]))
print('MSE on validation data: ', mean_squared_error(val_keypoint_op,val_pred[0]))
上面的代码片段给出了以下输出:
MSE on train data: 2.9205250961752722
MSE on validation data: 35.072992153148434
可视化验证集上的结果。
Output Result: Model 2
6.3 第三种模式的实施
这一个的代码如下:
inp = Input(shape=(160,160,3))#1st convolution pair
conv1 = Conv2D(16,kernel_size=(5,5), activation='relu')(inp)
mx1 = MaxPooling2D(pool_size=(2,2))(conv1)#2nd convolution pair
conv2 = Conv2D(48,kernel_size=(3,3), activation='relu')(mx1)
mx2 = MaxPooling2D(pool_size=(2,2))(conv2)#3rd convolution pair
conv3 = Conv2D(64,kernel_size=(3,3), activation='relu')(mx2)
mx3 = MaxPooling2D(pool_size=(2,2))(conv3)#4th convolution pair
conv4 = Conv2D(64,kernel_size=(2,2), activation='relu')(mx3)flt = Flatten()(conv4)dense = Dense(100,activation='relu')(flt)reg_op = Dense(10,activation='linear',name='key_point')(dense)pose_op = Dense(5,activation='softmax',name='pose')(dense)model = Model(inp,[reg_op,pose_op])model.summary()
编译模型。
loss_dic = {'key_point':'mse','pose':'categorical_crossentropy'}loss_weights = {'key_point':4,'pose':11}metrics = {'key_point':'mse', 'pose':['categorical_crossentropy','acc']}model.compile(optimizer='adam',loss=loss_dic,loss_weights=loss_weights,metrics=metrics)
一切就绪。让我们训练网络。
H = model.fit(train_images, [train_keypoint_op,train_categorical_ops[-1]], epochs = epochs, batch_size=bs, validation_data=(val_images,[val_keypoint_op,val_categorical_ops[-1]]),callbacks=[TrainValTensorBoard(log_dir='./log4',write_graph=False)])
让我们评价一下 model 的性能。
train_pred = model.predict(train_images)
val_pred = model.predict(val_images)print('MSE on train data: ', mean_squared_error(train_keypoint_op,train_pred[0]))
print('MSE on validation data: ', mean_squared_error(val_keypoint_op,val_pred[0]))
上面的代码片段给出了以下输出:
MSE on train data: 2.825882283863525
MSE on validation data: 31.41507419233826
可视化验证集上的结果。
Output Result: Model 3
6.4 第四种模式的实施
这一个的代码如下:
inp = Input(shape=(160,160,3))#1st convolution pair
conv1 = Conv2D(16,kernel_size=(5,5), activation='relu')(inp)
mx1 = MaxPooling2D(pool_size=(2,2))(conv1)#2nd convolution pair
conv2 = Conv2D(48,kernel_size=(3,3), activation='relu')(mx1)
mx2 = MaxPooling2D(pool_size=(2,2))(conv2)#3rd convolution pair
conv3 = Conv2D(64,kernel_size=(3,3), activation='relu')(mx2)
mx3 = MaxPooling2D(pool_size=(2,2))(conv3)#4th convolution pair
conv4 = Conv2D(64,kernel_size=(2,2), activation='relu')(mx3)flt = Flatten()(conv4)dense = Dense(100,activation='relu')(flt)reg_op = Dense(10,activation='linear',name='key_point')(dense)model = Model(inp,reg_op)model.summary()
编译模型。
loss_dic = {'key_point':'mse'}metrics = {'key_point':['mse','mae']}model.compile(optimizer='adam',loss=loss_dic,metrics=metrics)
一切就绪。让我们训练网络。
H = model.fit(train_images, train_keypoint_op, epochs = epochs, batch_size=bs, validation_data=(val_images,val_keypoint_op),callbacks=[TrainValTensorBoard(log_dir='./log5',write_graph=False)])
让我们评价一下 model 的性能。
train_pred = model.predict(train_images)
val_pred = model.predict(val_images)print('MSE on train data: ', mean_squared_error(train_keypoint_op,train_pred ))
print('MSE on validation data: ', mean_squared_error(val_keypoint_op,val_pred ))
上面的代码片段给出了以下输出:
MSE on train data: 2.822843715225789
MSE on validation data: 30.50257287238015
可视化验证集上的结果。
Output Result: Model 4
结论
根据上面的实验,很容易得出结论,多任务学习比单独解决这个问题更有效。
有时,解决多任务问题比单独解决问题更有帮助,但请注意,如果主问题依赖于辅助问题,则只解决辅助问题。
这个项目的完整源代码可以在 这里找到 。
希望,你喜欢这篇文章,如果你从中学到了什么新的东西,那么你可以通过与他人分享和关注我来展示你的爱…花了这么多时间来写这么全面的博文,希望我的努力能帮助你们中的一些人理解这个案例研究的细节,以便你们也能在自己的地方实施它。
欢迎在 LinkedIn 上与我联系,在 Twitter 和 Quora 上关注我。
使用 OpenCV 和 Python 在 2 分钟内完成人脸检测
在这篇短文中,我想分享一个在 OpenCV 和 Python 中使用 Haar 级联检测人脸的非常流行和简单的方法。
The video version for those who prefer that !
首先确保你已经安装了 OpenCV。您可以使用 pip 安装它:
pip install opencv-python
使用 Haar 级联的人脸检测是一种基于机器学习的方法,其中用一组输入数据训练级联函数。OpenCV 已经包含了许多预先训练好的人脸、眼睛、微笑等分类器…今天我们将使用面部分类器。您也可以尝试其他分类器。
需要下载训练好的分类器 XML 文件(Haar cascode _ frontal face _ default . XML),在 OpenCv 的 GitHub 资源库中有。将其保存到您的工作位置。
若要检测图像中的人脸:
需要注意一些事情:
- 该检测仅对灰度图像有效。因此,将彩色图像转换为灰度图像非常重要。(第 8 行)
- 检测多尺度函数(第 10 行)用于检测人脸。它有 3 个参数——输入图像、比例因子和最小邻居。比例因子指定图像尺寸随每个比例缩小多少。最小邻居指定每个候选矩形应该有多少邻居来保留它。你可以在这里详细了解。您可能需要调整这些值以获得最佳结果。
- 面包含找到面的矩形区域的坐标列表。我们使用这些坐标来绘制图像中的矩形。
结果:
同样,我们可以检测视频中的人脸。如你所知,视频基本上是由帧组成的,帧是静止的图像。因此,我们对视频中的每一帧进行人脸检测。代码如下:
这里唯一的区别是,我们使用一个无限循环来遍历视频中的每一帧。我们使用 cap.read() 来读取每一帧。返回的第一个值是一个标志,指示帧是否被正确读取。我们不需要它。返回的第二个值是我们将在其上执行检测的静止帧。
找到这里的代码:【https://github.com/adarsh1021/facedetection】
希望这对你有用。如果你在实现这个过程中有任何困难或者你需要任何帮助,请联系我。
电子邮件:adarsh1021@gmail.com
社交媒体: LinkedIn ,Twitter,insta gram, YouTube
8 行代码实现人脸检测、识别和情感检测!
用数据做酷事!
介绍
人类一直具有识别和区分人脸的先天能力。现在计算机也能做到这一点。这打开了大量的应用。人脸检测和识别可以用来提高访问和安全性,就像最新的苹果 Iphone 一样(见下面的 gif),允许在没有物理卡的情况下处理支付——Iphone 也是这样做的!,实现罪犯识别,并允许个性化的医疗保健和其他服务。人脸检测和识别是一个深入研究的课题,网上有大量的资源。我们已经尝试了多个开源项目,以找到最容易实现同时又准确的项目。我们还创建了一个管道,用于在图像加载后对任何输入图像进行检测、识别和情感理解,只需 8 行代码!我们的代码在 Github 上开源。
Facial Biometric
本博客分为三个部分:
- 面部检测—检测任何输入图像或帧中面部位置的能力。输出是检测到的面的边界框坐标
- 面部识别—将多张脸放在一起比较,以识别哪些脸属于同一个人。这是通过比较人脸嵌入向量来完成的
- 情绪检测——将脸上的情绪分为快乐、愤怒、悲伤、中性、惊讶、厌恶或恐惧
所以让我们开始吧!
面部检测
面部检测是我们管道的第一部分。我们使用了 python 库人脸识别,我们发现它易于安装,并且在检测人脸时非常准确。该库扫描输入图像,并返回所有检测到的面的边界框坐标,如下所示:
Face Detection
下面的代码片段展示了如何使用 face_recognition 库来检测人脸。
face_locations = face_recognition.face_locations(image)
top, right, bottom, left = face_locations[0]
face_image = image[top:bottom, left:right]
安装和使用面部识别的完整说明也在 Github 上
面部识别
面部识别验证两张脸是否相同。面部识别在安全、生物计量、娱乐、个人安全等方面的应用非常广泛。用于人脸检测的同一个 python 库 face_recognition 也可以用于人脸识别。我们的测试表明它具有良好的性能。给定两张匹配的脸,它们可以相互匹配,给出真或假的结果。面部识别涉及的步骤有
- 在图像中查找人脸
- 分析面部特征
- 比较两个输入面的特征
- 如果匹配则返回真,否则返回假。
下面是实现这一点的代码片段。我们为两个人脸创建人脸编码向量,然后使用内置函数来比较向量之间的距离。
encoding_1 = face_recognition.face_encodings(image1)[0]
encoding_2 = face_recognition.face_encodings(image1)[0]
results = face_recognition.compare_faces([encoding_1], encoding_2,tolerance=0.50)
让我们在下面两张图片上测试模型:
Face 1
Face 2
如右图所示,我们有两张不同姿势的莱昂纳多·迪卡普里奥的脸。在第一张照片中,脸部也不是正面照。当我们使用上面共享的代码运行识别时,人脸识别能够理解这两张脸是同一个人!
情感检测
人类习惯于从面部情绪中获取非语言暗示。现在计算机也越来越擅长阅读情感。那么我们如何在图像中检测情绪呢?我们使用了来自 Kaggle 的开源数据集——人脸情绪识别(FER )并构建了一个 CNN 来检测情绪。情绪可以分为 7 类——快乐、悲伤、恐惧、厌恶、愤怒、中性和惊讶。
模型——我们在 Keras 中构建了一个 6 层卷积神经网络(CNN ),并使用图像增强来提高模型性能。我们尝试了许多不同的模型,并在这个链接上开源了我们的最佳实现。
您可以加载预训练的模型,并使用下面的两行代码在映像上运行它:
model = load_model("./emotion_detector_models/model.hdf5")
predicted_class = np.argmax(model.predict(face_image)
结论
这篇博客演示了在你的应用程序中实现面部检测和识别模型是多么容易。面部检测可以是许多定制解决方案的起点。我希望你自己尝试一下我们的开源代码。
我有自己的深度学习咨询公司,喜欢研究有趣的问题。我已经帮助许多初创公司部署了基于人工智能的创新解决方案。请到 http://deeplearninganalytics.org/来看看我们。如果你有一个我们可以合作的项目,那么请通过我的网站或在info@deeplearninganalytics.org联系我
你也可以在https://medium.com/@priya.dwivedi看到我的其他作品
参考文献:
面部/关闭:使用 Viola Jones 方法的高速面部跟踪
有没有想过你数码相机上的盒子是如何准确地知道一张脸在哪里和是什么的?
早在 2011 年,在卷积神经网络或任何其他类型的深度学习技术流行之前,就有了 Viola-Jones 算法。这种算法是如此的创新和有效,以至于这篇论文被引用了 19000 多次。
研究人员的目标是解决面部检测的两个最大问题。该算法实现了以下功能:
- 鲁棒性—高检测率(真阳性率)和极低的假阳性率。
- 速度—对于实时,每秒必须处理至少两帧。
最大限度地利用这两个条件将需要大量的处理能力,而这在 2001 年是不具备的。即使是现在,如果我们希望能够在一个便宜的设备上运行面部检测,例如 Raspberry Pi,这种权衡将是一个优先事项。
显然需要某种创新的方法来实现鲁棒性和速度之间的平衡,从而返回准确的结果。
现在让我们来看看维奥拉和琼斯是如何实现这个目标的。
分类器
研究人员发现了一个过程,在这个过程中,你可以非常成功地识别图像的子窗口是否是一张脸。
首先,图像被转换成灰度。然后,为了识别图像中的一张脸,Viola 和 Jones 决定使用所谓的 Haar Like 特征。这些是图像的子窗口,其中一部分比另一部分暗。
然而,这是一个弱分类器。例如,具有两种对比色的盒子可以被视为一张脸,因为在个人的眼睛和脸颊之间经常设置相似的对比色。
将选择数以千计的这些特征,并且如果它们被证明是真实的,我们将能够以高精度决定图像的子窗口是否包含人脸。
积分图像
该算法最聪明的部分是它如何达到它的速度。为了有效地计算特征,它使用所谓的积分图像。
Via Matlab
当一个新的图像被分类时,像素值的总和被跨越行和列,使得像素(I,j)是在它之前的所有像素的总和。
对图像进行这种计算预处理允许计算机通过一些加法和减法运算来计算矩形特征,而不是遍历像素值。
现在,当算法测试图像的一部分是否是特征时,它可以很容易地在常数时间内取矩形区域的差。
分类器学习/选择
这些矩形框的原始特征数量是天文数字。根据这篇研究论文,仅仅在一个 24x24 的图像窗口中就有 162,336 个特征。
由于每个矩形本身都是一个弱分类器(每个矩形的表现都比猜测略好),Viola 和 Jones 使用了自适应增强(AdaBoost)。
Zhuo Wang — Realizing Low-Energy Classification Systems by Implementing Matrix Multiplication Directly Within an ADC
AdaBoost 使用所有矩形特征,并根据训练数据集训练它们。在此过程中,它将通过对弱分类器进行线性组合来寻找弱特征组并使其成为分类器。
简而言之,AdaBoost 构建的每个分类器都是一组固定数量的特征,每个特征都有不同的权重,以确定其形成的分类器的二元分类决策。使用由提升方法形成的这些分类器而不是每个单独的特征,为模型提供了更强的图像分类方法。
注意力级联
为了区分一个图像的子窗口实际上是不是一张脸,他们建立了一个注意力级联。它本质上是一棵决策树,其中每个节点都是所构造的分类器之一。
Via Matlab
级联的节点从简单的两个特征分类器开始排序,具有非常高的检测率,但是具有高的假阳性率。随着我们的前进,树节点变成一个更复杂的分类器,这将给我们更多的准确性,但在计算上更复杂。
Via stack overflow
图像的每个子窗口对照树中的第一个分类器进行测试,并且或者被分类为“可能是一张脸”,这将其移动到下一个分类器,或者被分类为“不是一张脸”,并且将不会用下一个分类器进行测试。
每个分类器的计算时间取决于组成分类器的特征数量。因此,该树从具有高检测率的简单分类器开始,该分类器通过将大量子窗口分类为注意力级联的早期节点中的非人脸来丢弃它们。
研究人员训练的模型最终形成了一个 38 层的级联分类器树。
他们的模型结果相当出色。该论文指出,在 700 Mhz 奔腾 III 处理器上,面部检测器可以在大约 0.067 秒内处理 384×288 像素的图像。
结论
尽管在图像识别领域已经取得了许多进步,但这表明这种算法虽然仍是机器学习,但比 CNN 更容易解释。在算法的每个阶段,你都能够准确地解释算法实际在做什么。
在实践中,我们可以优化实时识别和跟踪的一种方法是使用 Viola Jones 来检测面部,然后使用像 Kanade–Lucas–Tomasi(KLT)特征跟踪器这样的算法来跟踪视频中检测到的面部。
如果你有兴趣自己尝试一下,OpenCv 有一个实现,我将在下面链接,还有 Viola 和 Jones 的原始论文以及 Mike Pound 博士在 Computerphile 上的一个非常有用的解释。
https://www . cs . CMU . edu/~ efros/courses/lbmv 07/Papers/viola-cvpr-01 . pdf
使用基于 Haar 特征的级联分类器的目标检测是由 Paul…
docs.opencv.org](https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_objdetect/py_face_detection/py_face_detection.html)
使用 JavaScript API 的人脸检测— face-api.js
Face-API.js
在本文中,我们将学习使用face-api.js
的面部检测(年龄/性别/面部位置/情绪),以及在网络浏览器上使用coco-ssd
模型的附近物体检测(人/电话等)。
face-api.js
是一个 javascript 模块,构建在 tensorflow.js 核心 之上,实现了几个 CNN s ( 卷积神经网络)来解决人脸检测、人脸识别和人脸地标检测,针对 web 和移动设备进行了优化。
让我们现在开始,
先决条件:
- 对 React.js 有基本的了解(你可以选择任何你喜欢的库或前端框架)
- 对 p5.js 库的基本了解。
- 已安装的 create-react-app 和 Node.js 版本> = 10.15.1
让我们创建一个 react 项目,
npx create-react-app object_face_detection
cd object_face_detection
现在,安装下面的依赖项
npm install @tensorflow-models/coco-ssd
npm install @tensorflow/tfjs-converter
npm install @tensorflow/tfjs-core
npm install face-api.js
npm install p5
npm install react-p5-wrapper
让我们一个一个地了解每一个依赖关系——
@tensorflow-models/coco-ssd
—这将用于其他物体检测,如电话、墙壁等。在脸的周围。Coco-ssd
是一个 TensorFlow 模型,已经用大量通用图像进行了训练,可以直接在浏览器中使用。你可以在这里阅读—https://github . com/tensor flow/tfjs-models/tree/master/coco-SSD@tensorflow/tfjs-converter
—将 TensorFlow 保存的模型和 keras 模型直接转换为 tensorflow.js 使用。我的意思是已经有很多使用 python 或 R 制作/训练的模型,但是模型保存的格式不同于 TensorFlow.js 使用/消费的格式。所以需要这种依赖来将其他模型转换成 TensorFlow 的可消费格式。@tensorflow/tfjs-core
— Tensorflow 核心 javascript 库。你可以读读这个——https://www.tensorflow.org/js/tutorials/setup。face-api.js 是在这种依赖的基础上构建的。face-api.js
—这是本文的核心 API,将用于人脸检测。了解更多—https://github.com/justadudewhohacks/face-api.js?files=1- 这是最近发展起来的另一个伟大的库,在我们的上下文中,我们将使用它来制作网络摄像头视频,并在检测到的人脸和物体周围绘制一个红色框。你可以阅读—https://p5js.org
react-p5-wrapper
—这只是一个在 p5js 功能上编写的 reactjs 包装器。所以与其写一个,不如用它来节省时间。
现在让我们深入编码💻
在我们开始之前,我们需要下载face-api.js
模型(已经建立做面部和情绪检测)。因此,让我们在我们的public
文件夹中创建一个名为models
的文件夹,并下载位于https://github . com/justadudewhohacks/face-API . js/tree/master/weights的文件
models
文件夹将会是这样的
我们现在将在我们的src
文件夹中创建一个名为ObjectDetectionSketch.js
的文件,它将包含我们所有的逻辑。
该文件将有如下一些导入语句
import * as p5 from 'p5'import "p5/lib/addons/p5.dom";import * as cocoSsd from '@tensorflow-models/coco-ssd';import * as faceapi from 'face-api.js';
- p5 和 p5.dom —需配合 p5js,忍着点,过几段你就明白确切用法了。
- cocoSsd 和faceapi——你现在已经知道了。
接下来,我们将定义我们的 face-API 模型 URL
const MODEL_URL = '/models'
// this will pick public folder by default
现在,我们将创建名为 sketch 的函数(包含所有逻辑的包装函数)
export default function sketch (p) {}
在 sketch 函数内部,我们将定义几个变量和四个函数,两个自定义的,两个来自 p5.js,分别叫做setup
和draw
。
变量
// Variables// save current camera image
let capture = null;// save cocossd Model
let cocossdModel = null;// to save the result of cocossd and face-api results
let cocoDrawings = [];
let faceDrawings = [];
自定义功能
// Custom Function// Used to store the result of coco-ssd model
function showCocoSSDResults(results) {
const id = capture.id();
cocoDrawings = results;
}// used to store the result for the face-api.js model
function showFaceDetectionData(data) {
faceDrawings = data;
}
P5.js 函数
// P5.js Functions
p.setup = async function() {}
p.draw = function() {}
我们来详细了解一下这两个 p5 的功能。🚀
Setup
功能
页面加载后,将自动调用 p5.js 设置。我们正在覆盖 p5 内置的setup
函数来初始化我们需要的一些细节。下面是我们将在设置函数中执行的步骤。
1。加载我们将用于人脸检测的三个 face-api.js 模型。
await faceapi.loadSsdMobilenetv1Model(MODEL_URL);
await faceapi.loadAgeGenderModel(MODEL_URL);
await faceapi.loadFaceExpressionModel(MODEL_URL);
2 。创建一个 p5.js 画布
p.createCanvas(1280, 720);
3 。在画布上实现摄像头捕捉功能。
const constraints = {
video: {
mandatory: {
minWidth: 1280,
minHeight: 720
},
optional: [{ maxFrameRate: 10 }]
},
audio: false
};capture = p.createCapture(constraints, () => {});
4。 设置视频 Id 和大小。
capture.id("video_element");
capture.size(1280, 720);
capture.hide(); // this is require as we don't want to show the deafault video input
5 。加载 cocoSsd 模型并保存在本地。
cocoSsd.load().then((model) => {
try {
cocossdModel = model;
} catch(e) {
console.log(e);
}
}).catch((e) => {
console.log("Error occured : ", e);
});
绘图功能
如果在 p5js 画布上绘制任何东西,就会调用 p5js 的 draw 函数。在我们的自定义绘制函数中,我们将执行以下步骤。
- 将背景设置为白色,并在上面绘制我们的图像。此外,添加透明度,这样任何进一步绘制到画布上的东西都将是透明的。
p.background(255);
p.image(capture, 0, 0);
p.fill(0,0,0,0);
2.代码渲染 coco-ssd 模型结果。
cocoDrawings.map((drawing) => {
if (drawing) {
p.textSize(20);
p.strokeWeight(1);
const textX = drawing.bbox[0]+drawing.bbox[2];
const textY = drawing.bbox[1]+drawing.bbox[3];
const confidenetext = "Confidence: "+ drawing.score.toFixed(1);
const textWidth = p.textWidth(confidenetext);
const itemTextWidth = p.textWidth(drawing.class);
p.text(drawing.class, textX-itemTextWidth-10, textY-50);p.text(confidenetext, textX-textWidth-10, textY-10);
p.strokeWeight(4);
p.stroke('rgb(100%,100%,100%)');
p.rect(drawing.bbox[0], drawing.bbox[1], drawing.bbox[2], drawing.bbox[3]);
}
});
这里我们有一个cocoDrawings
对象,包含 coco-ssd 模型检测到的当前对象细节。这个物体的形状看起来像
{
"bbox": [
6.165122985839844,
2.656116485595703,
1034.7143936157227,
712.3482799530029
],
"class": "person",
"score": 0.9296618103981018
}
我们使用这个对象数据来绘制一个矩形,该矩形定义了当前对象的位置以及被检测到的对象的名称(在上面的例子中是人)和分数。
这是一个基本的 p5js 代码,用于绘制文本和矩形。如果你觉得很难理解,那么试试 p5.js docs,一个小时之内你就会明白了。—https://p5js.org/
我们可以在画布上绘制多个对象,因为它们会被检测到。
3.代码呈现 face-api.js 模型结果。
faceDrawings.map((drawing) => {
if (drawing) {
p.textSize(15);
p.strokeWeight(1); const textX = drawing.detection.box._x+drawing.detection.box._width;
const textY = drawing.detection.box._y+drawing.detection.box._height;
const confidenetext = "Gender: "+ drawing.gender;
const textWidth = p.textWidth(confidenetext);
p.text(confidenetext, textX-textWidth, textY-60); const agetext = "Age: "+ drawing.age.toFixed(0);
const ageTextWidth = p.textWidth(agetext);
p.text(agetext, textX-ageTextWidth, textY-30); const copiedExpression = drawing.expressions;
const expressions = Object.keys(copiedExpression).map((key) => {
const value = copiedExpression[key];
return value;
}) const max = Math.max(...expressions);
const expression_value = Object.keys(copiedExpression).filter((key) => {
return copiedExpression[key] === max;
})[0]; const expressiontext = "Mood: "+ expression_value;
const expressionWidth = p.textWidth(expressiontext);
p.text(expressiontext, textX-expressionWidth, textY-10);
p.strokeWeight(4);
p.stroke('rgb(100%,0%,10%)');
p.rect(drawing.detection.box._x, drawing.detection.box._y, drawing.detection.box._width, drawing.detection.box._height);
}
});
这里我们定义文本大小,将从 face-api.js 获得的数据绘制到 p5.js 画布上。
现在,每个检测到的人脸都有以下由 face-api.js 模型返回的数据
{
"detection": {
"_imageDims": {
"_width": 1280,
"_height": 720
},
"_score": 0.6889822483062744,
"_classScore": 0.6889822483062744,
"_className": "",
"_box": {
"_x": 121.50997161865234,
"_y": 15.035667419433594,
"_width": 507.80059814453125,
"_height": 531.7609024047852
}
},
"gender": "male",
"genderProbability": 0.9683359265327454,
"age": 30.109874725341797,
"expressions": {
"neutral": 0.9950351715087891,
"happy": 0.0000017113824242187547,
"sad": 0.000005796719960926566,
"angry": 0.00000466804613097338,
"fearful": 1.3292748013427058e-9,
"disgusted": 3.015825145169515e-9,
"surprised": 0.004952521994709969
}
}
你可以看到,我们得到的是一个矩形坐标的人脸、性别、年龄和表情数据
我们可以从detection._box
中提取矩形坐标,对于表达式,我们有所有的表达式及其相应的分数。所以,
const copiedExpression = drawing.expressions;
const expressions = Object.keys(copiedExpression).map((key) => {
const value = copiedExpression[key];
return value;
})const max = Math.max(...expressions);const expression_value = Object.keys(copiedExpression).filter((key) => {
return copiedExpression[key] === max;
})[0];
用上面的代码,我们将估计和获得高度记分员表达式,并显示在一个矩形内
最困难的部分是将文本放入矩形,所以我们做了一个不太好的实现,但它是可行的。
所以我们从盒子的 x 坐标上去掉这个宽度,再加 10,这样左边界和显示文本就有了一些空白。
const ageTextWidth = p.textWidth(agetext);
p.text(agetext, textX-ageTextWidth, textY-30);
等一等🤔这一切都很好,但是检测人脸和其他物体的代码在哪里呢?
这就是了👇
4.检测人脸和其他元对象的代码。
faceapi.detectAllFaces(capture.id())
.withAgeAndGender()
.withFaceExpressions()
.then((data) => {
showFaceDetectionData(data);
});if(capture.loadedmetadata) {
if (cocossdModel) {
cocossdModel
.detect(document.getElementById("video_element"))
.then(showCocoSSDResults)
.catch((e) => {
console.log("Exception : ", e);
});
}
}
我们结束了🎢准备测试。
在候机楼里面做什么
cd object_face_detection
npm start
这是尝试的结果—
https://github . com/overflow js-com/object _ face _ detection _ web cam _ react——如果你觉得很难理解本文的内容,可以参考这里的代码。
注意:这个实现很慢,因为我们在浏览器中加载所有的模型,并且是实时的
如果您想被添加到我的电子邮件列表中,请考虑在这里输入您的电子邮件地址 和关注我的 medium 阅读更多关于 javascript 的文章,并关注github查看我的疯狂代码。如果有什么不清楚或者你想指出什么,请在下面评论。
你可能也会喜欢我的其他文章
- Javascript 执行上下文和提升
- Javascript —生成器-产出/下一个&异步-等待🤔
- 理解 Javascript‘this’关键字(上下文)。
- Javascript 数据结构与映射、归约、过滤
- Javascript- Currying VS 部分应用
- Javascript ES6 —可迭代程序和迭代器
- Javascript —代理
- Javascript —作用域
如果你喜欢这篇文章,请随意分享,以帮助他人找到它!
脸书 vs .欧盟人工智能和数据政治
Photo by @greystorm
关于人工智能和脸书数据政治的新 FRA 论文
本文是欧盟基本权利机构(FRA)的一篇名为 数据质量和人工智能——减轻偏见和错误以保护基本权利 的论文的摘要。然后,我继续研究脸书最近在数据政治方面的举措;扎克伯格发表的声明;以及他们最近聘请前副首相尼克克莱格(Nick Clegg)担任全球政策和沟通主管。我希望这能使欧盟的政策更容易理解,并向你们概述脸书在这方面采取的一些行动。
什么是 FRA?
欧盟基本权利署是一个欧盟机构,其任务是“……收集和分析原则上涉及《宪章》所列所有权利的基本权利数据”,这里指的是《欧洲联盟基本权利宪章》。《宪章》规定的这些权利包括:尊严、自由、平等、团结、公民权利和正义。
FRA 关于数据质量和人工智能的论文
FRA 有一个关于人工智能、大数据和基本权利的项目于 2018 年启动。该报告评估了在选定的欧盟成员国将人工智能(AI)和大数据用于公共管理和商业目的的基本权利的利弊。2019 年 6 月发表的论文 数据质量和人工智能——减少偏见和错误以保护基本权利 是这个项目的一部分,也是我在这一部分的重点。
这篇论文是对正在进行的人工智能和大数据政策讨论的贡献。本部分中的引用将全部来自本文。
该报告声称,机器学习系统和人工智能(AI)中使用的算法只能与用于开发它们的数据一样好。然而,关键问题是如何定义’质量’。他们强调透明度,认为数据量往往比质量更重要。
人工智能包含在欧盟委员会关于欧洲人工智能的通信中:“人工智能(AI)是指通过分析其环境并采取一定程度的自主行动来实现特定目标的智能行为的系统。
他们提到 AI 不是指一个事物,而是一套流程和技术发展。
在论文中,数据质量是一个宽泛的概念。他们强调了社会科学和调查研究中使用的与数据质量有关的两个通用概念:
- 表示错误,这意味着数据没有很好地覆盖它应该覆盖的人群;
- 测量误差 ,这意味着数据并没有测量出它们想要测量的东西。
解释复杂算法如何工作一直是一个焦点,但是有人认为 ML 和数据科学忽略了数据质量的关键方面。
其他报告和欧洲理事会多次提到这个问题。欧洲科学和新技术伦理小组指出,应避免数据集中的歧视性偏见。
欧盟委员会人工智能高级专家组在其道德准则中将数据治理作为可信人工智能的要求之一。
由人权和技术组织联盟撰写的“多伦多宣言”将一种风险命名为“不完整或不具代表性的数据,或代表历史或系统偏见的数据集”。
该报告提到了涉及至少三个不同数据集的机器学习(ML)算法:
- 训练数据。在监督学习中,用于学习期望结果的数据是所谓的特征*。期望的结果通常被称为标签。这是算法如何学习模式的基础。*
- 输入数据。当一个算法被部署时,新的看不见的特征被添加。
- 推断标签(或预测、推论、推断动作或输出数据)当看不见的数据馈入 ML 算法时产生。
在这篇论文中,他们提出了一个案例研究,重点是来自互联网的数据,以说明“表示错误”的可能性。
该报告提到了欧盟对大数据的使用。总体而言,欧盟三分之一的大型企业(33 %)使用大数据分析。数据的使用引发了问题。他们列出了以下内容:
并非所有人:
- 能接触到社交媒体,报道也不尽相同。
- 想要访问互联网、社交媒体或其他应用程序。
有些群体不包括在收集的数据中,只包括那些放弃数据的人。下图显示了数字鸿沟,通常被称为数字鸿沟。资料来源:2019 年欧洲森林资源管理局【根据欧统局(ISOC _ ci _ in _ h)】:
还有一个地理上的差异。因此,收集的数据不代表人口中的某些群体。
然后,他们继续讨论基本权利,每个权利都有一个简短的例子:
- 不歧视。
- 男女平等。
- 获得公平审判和有效补救。
- 私人和家庭生活
- 个人资料的保护
*研究人员已经开始分析这个话题,强调将推断的
数据视为个人数据的重要性。只有这样,GDPR 的权利才能适用,包括了解这些数据的权利,以及访问、纠正、删除和反对这些数据的权利。*
对于自动化决策,GDPR 要求数据控制者提供关于所涉及的逻辑的有意义的信息,以及这种处理对于数据主体的重要性和预期后果。
评估数据质量
存在着完整性、准确性、一致性、及时性、复制性、有效性、可用性和出处等问题。提到的一个具体问题是:
- 大规模移民中的生物特征数据。
数据可能不准确,对技术的信任可能导致不幸的情况。“数据质量”的一个定义是所使用的数据是否“符合目的”。
“因此,数据的质量在很大程度上取决于它们的使用目的.”
文中提到了两种常见的误差来源。这是在经典调查研究的背景下讨论的:测量误差和表示误差。
测量误差指的是所使用的数据指示或反映要测量的内容的准确程度。
必须如何评估回答者?编辑和重组数据对此有多大影响?你如何衡量一个好员工?作为一个评论,我可以补充:你如何衡量一个好用户?
数据近似于真实世界的现象,测量中总会有误差。多大的误差是可以接受的?数据通常由人工标记(如标记图片),因此标记过程中的质量控制至关重要。
表示错误:如果数据没有很好地覆盖它应该覆盖的人群,那么得到的统计数据将是不正确的(即有偏见的)。
一个重要的问题是,用于构建应用程序的数据是否能够准确地代表未来的用户。人们通常会开始以不同的方式行事。
FRA 的信度和效度
社会科学中用来描述某些概念的测量误差的两个概念。可以通过指数等指标来完成;或者当无法直接测量时,使用相关问题的测量。
***可靠性:*指测量的稳定性和一致性。
***有效性:*数据和预测是否实际测量了它们打算测量的东西的问题,因此与表示和测量的误差有关。
不可靠的数据可能有正确的目标,但显示出太多的变化和不确定性,从而错过了目标,尽管平均结果良好。大的数字减少统计的不确定性,然而低的质量可能不增加有效性。
欧洲统计系统中的 FRA 数据质量——人工智能的教训
根据本文,数据质量是一项竞争优势。本文引用了欧洲统计系统的质量减速,我发现将它们包括在内很有价值:
-
质量方针是公开的。
-
*制定了计划和监控数据生产流程的
质量的程序。*
-
*定期监控、评估
和报告产品质量。*
-
定期对输出进行全面评审,包括由外部专家进行评审。
这种统计数据的产生与人工智能相关,因为它们与所使用的机器学习方法相关。在该论文中,提出了统计学和机器学习之间的主要区别如下。统计学旨在描述具有特征、相关性和因果解释的群体。根据洗钱司的意见,案文内容如下:
……机器学习主要关注的是预测一个单位的特征,比如一个人、一个公司或者一个国家。这具有稍微不同的含义,因为与关于人口群体的一般统计相比,预测的准确性变得更加重要。
质量评估数据集描述—行业标准
人工智能和人工智能领域的专家提出了数据集描述,称为‘数据表’,或‘营养标签’。有人建议以与硬件组件相同的方式描述数据集,以检查它们是否符合行业标准。这篇论文声称,在人工智能领域,没有标准化的方法来描述数据集。
这种标准化必须考虑到灵活性,以便能够包括人工智能应用中使用的各种可能的数据格式和集合。这一点很重要,因为如果数据是为一个目的生成的,就需要评估它们是否也适合另一个目的。
数据文档倡议(DDI)有一个描述数据集(即元数据)的标准化方法。在某种程度上,这对于共享以供重用是有用的。需要数据收集的背景、方法和元级描述。通过这种方式,使用特定数据集评估工具的潜在误差将更加容易。因此,本文认为统计实践为人工智能方面的数据质量保证提供了潜在的途径。
FRA 结论
利用数据的 ML 系统和算法需要更广泛和更灵活的方法来评估和处理数据质量。他们建议询问几个问题,以确定算法使用中的质量问题:
- 数据从哪里来?谁负责数据收集、维护和传播?
- 数据中包含哪些信息?数据中包含的信息是否适合算法的目的?
- 数据涵盖了哪些人?谁在数据中被低估了?
- 数据集内是否缺少信息,或者是否只覆盖了部分单元?
- 用于构建应用程序的数据收集的时间框架和地理覆盖范围是什么?
由于质量可能会对歧视性做法和错误系统产生影响,因此缓解潜在问题非常重要。该论文认为,这可以借鉴社会科学和调查研究的“严谨性”。然而他们说:
“新技术需要对潜在的基本权利挑战进行全面评估[……]为了更好地理解对基本权利的影响,需要进行跨学科研究,因为这一主题结合了许多不同领域的要素,包括法律、计算机科学、统计学和社会科学。”
脸书及其数据政治
剑桥分析报告发布后,欧盟美国联邦贸易委员会同意对脸书罚款 50 亿美元以解决隐私侵犯问题。据《卫报》2019 年 7 月 12 日报道,这将是联邦贸易委员会对一家技术公司的最大一笔罚款,也是对任何一家侵犯隐私的公司的最大一笔罚款。
通用数据保护条例(GDPR)
GDPR 于 2018 年 5 月 25 日生效,是“世界上最严厉的隐私和安全法”。在欧盟的 GDPR 页面上写着:
…违反 GDPR 的罚款非常高。处罚分为两级,最高为€2000 万英镑或全球收入的 4%(以较高者为准),此外,数据主体有权寻求损害赔偿
看到脸书的例子后,我们可以理解这不仅仅是一个大胆的主张。该文章提到:
- 个人数据:与个人相关的任何可以直接或间接识别的信息。
- 数据处理:对数据执行的任何动作,无论是自动还是手动。
- 数据主体:数据被处理的人。
- 数据控制者:决定为什么以及如何处理个人数据的人。
- 数据处理者:代表数据控制者处理个人数据的第三方。例如服务器。
特别是,在同意方面有额外的严格规则,使其在更大程度上可以理解。还有三种情况可能需要任命一名数据保护官员(DPO): (1)如果你是一个公共机构;(2)大规模系统地监控人;(3)大规模处理特殊类别的数据(定罪和医学)。法规本身有 88 页,我在这里没有提供足够好的概述。
脸书在 GDPR
在“服从”这个表达中,脸书似乎给在 GDPR 上创建了一个页面。他们在几个段落中概述了关键词。然后,他们继续采取行动,他们将采取遵守。
- 透明度:脸书的数据政策定义了他们如何处理人们的个人数据。他们承诺提供有关其数据政策的教育。他们将通过(1)产品内通知和(2)消费者教育活动来确保人们了解他们的数据如何被使用以及他们的选择。
- 控制:提供对数据使用方式的控制。启动控制中心,使隐私设置更容易理解和更新。它们提醒人们在使用脸书时如何查看和编辑他们的设置。
- 责任:脸书有隐私原则解释他们如何看待隐私和数据保护。他们有一个团队来帮助确保我们记录我们的合规性。此外,他们定期会见来自世界各地的监管者、决策者、隐私专家和学者,通报他们的做法。
扎克伯格拥抱 GDPR 并雇佣政策专家
马克·扎克伯格最近积极拥抱 GDPR,尽管他的动机受到质疑。然而,扎克伯格并不是第一个支持 GDPR 式规则的科技公司首席执行官。微软首席执行官塞特亚·纳德拉称赞欧洲法律和蒂姆·库克去年呼吁联邦隐私监管。其他国家,如加拿大,现在开始考虑是否能让脸书参与隐私问题的讨论。
最近,脸书聘请英国前副首相尼克克莱格爵士(Sir Nick Clegg)担任其全球政策和沟通主管。这可能是由于公司在过去几年中面临的一系列问题。这包括但肯定不限于:
马克·扎克伯格呼吁在四个不同领域进行监管。有害内容、完整性、隐私和数据可移植性是他概述的关键问题:
有害内容——他希望社交应用能有总体的规则和基准来衡量
选举诚信——他希望政府对什么构成政治或议题广告做出明确的定义
隐私——他想要 GDPR 式的全球法规,对违反者进行制裁
数据可移植性——他希望用户能够将他们的信息从一个应用程序带到另一个应用程序
尼古拉斯爵士威廉彼得克莱格
2019 年 7 月 17 日新政治家报道称,Nick 作为一名翻译有着积极的看法,他的方法是:“忽略意识形态和党派偏见;求进步,求妥协;寻找基于证据和现实解决方案”他能流利地说五种语言:英语、西班牙语、法语、德语和荷兰语。
此外,负面的一面是,他会为了利益不择手段。克莱格一家住在门洛帕克一栋 700 万英镑的豪宅里,根据《福布斯》的说法,这是美国“最昂贵的邮政编码”。《卫报》的一篇观点文章称他“在学费问题上背叛了公众和他在学生城的核心选票”。作为一名学生,我在英国生活了三年,我觉得这反映了相当多的英国年轻人的情绪。
他最近在欧洲其他国家的首都旅行,要求政府对脸书进行监管。克莱格去了威斯敏斯特学校,在剑桥学习人类学。他声称传统媒体比新媒体对政治的影响更大,声称没有证据表明俄罗斯干涉英国选举。他说脸书希望被监管。
脸书和中国
正如 Nick Clegg 在他的观点中所提出的,脸书现在正在争论它作为一个抗衡中国的力量的地位,以及反对‘专制社会价值观’的个人权利。这一点最近已经为人所知,因为一个关于卡利布拉推动引入新货币的听证会。脸书 Calibra 的负责人 David Marcus 说:
***我相信,如果美国不在数字货币和支付领域引领创新,其他人会的。*如果我们不采取行动,我们很快就会看到一种由价值观截然不同的人控制的数字货币。
不到一年前,2018 年 10 月,马克·扎克伯格在 Recode 的一次采访中对卡拉·斯威舍说:
我们在这里长大,我认为我们分享了很多我认为这里的人们非常珍视的价值观,我认为我们这样做总体来说是非常好的,无论是出于安全原因还是从价值观的角度来看。因为坦率地说,我认为另一个选择将是中国公司。如果我们采取这样的立场,“好吧,作为一个国家,我们将决定,我们要切断这些公司的翅膀,让它们更难在不同的地方运营,它们必须变得更小,”那么就会有很多其他公司愿意并且能够取代我们正在做的工作。
尼克·克莱格在 2019 年 1 月重复了这条信息。2019 年 7 月 19 日,TechCrunch 发表了一篇全面的文章。文章的结论是响亮的:“也许援引中国的威胁来煽动政府官员的担忧是政治上的精明,甚至可能是有效的。这并不意味着它是正确的。”以这种方式,现在的问题是一家大公司及其作为一家公司的公民责任或地位。**
公民责任和数据政治
面对越来越多的监管和指控,以及脸书方面以这种方式做出的回应,很难理解下一步该何去何从。然而,我们可能会开始考虑,当一家公司变得如此庞大,有如此多的在线用户以各种方式讨论他们的生活时,会发生什么。
不久前的一次聚会上,我听到有人说:
“这些大型科技公司有时和国家一样大。也许他们应该开始这样做,并承担一些公民责任。”
对大多数人来说,脸书作为一个国家行为者或具有类似国家的品质似乎有些牵强,包括我在内,但这是一种值得考虑的可能性。公私合作制定公平的数据政策,既保护人民,又为创新敞开大门,这不是一件容易的事情。
我们可以大胆地进入福柯式的概念,如生物政治、生物勘探和抵抗——是的,我们当然可以问权力的问题。政治和公司的分离差距越来越大,在国际关系(IR)的意义上,这种观点的相关性越来越大。甚至在涉及国际关系的基本概念时。
这是现实主义、自由主义还是社会建构主义?看起来脸书的数据政策正在从自由主义的“自由”向 T2 提出的“基于现实的解决方案”的方向发展。两者都同样模糊不清。
从数据主体到数据公民
从私人权力到国家权力的问题,以及随之而来的困惑,在这个、【结尾】或中,看看数据政治的学术定义会很有趣。
Evelyn RUP pert、Engin Isin 和 Didier Bigo 于 2017 年 12 月在 Researchgate 上发表的一份报告中定义了数据政治:
我们将*‘数据政治’*定义为关于这些世界的政治问题的表达,以及它们通过提出权利主张来激发主体管理自己和他人的方式。我们认为,如果不理解这些可能性条件——世界、主体和权利——就很难干预或塑造数据政治,如果这意味着将数据主体转变为数据公民的话。
因此,这种表达当然是说明有数据公民身份,他们还没有定义这是隐含的。这些权限是在文章中累积数据的上下文中定义的。提交人质疑公民是否有权知道谁:
- 拥有
- 分配
- 卖
- 接近
- 使用
- 挪用
- 修改
- 辞职
因此,数据公民权意味着公民义务。如果您有权使用您的数据,并且您拥有这些数据,那么您还有责任管理您的数据。你行使权力或控制——指导该领域政策的制定和管理。这不仅是 GDPR 的敌人,也让人们有权利增加责任感和义务感。
“……每一项权利都意味着一项责任;每一个机会,一种义务;每一份财产,都是一种责任
我们必须尽最大努力了解数据公民身份,以及我们在这方面有哪些权利、责任和义务。特别是在人工智能和数据政策的背景下,这两个领域在国际和当地的讨论中受到越来越多的关注。
这是第 500 天的第 48 天
什么是#500daysofAI?
我在挑战自己,写下并思考未来 500 天的人工智能话题。一起学习是最大的快乐,所以如果你觉得一篇文章引起了你的共鸣,请给我反馈。感谢您的阅读!
FaceMath:人工智能中的思想代数。
人工智能的无监督学习
变换面孔、文字和其他很酷的东西
闭上眼睛,在脑海中想象一个洋娃娃。现在想象同一个娃娃会说话。看娃娃说话。你刚才在脑子里所做的——你能看到会说话的东西——是一个娃娃的想法和会说话的想法的结合。事实证明,我们可以使用人工智能通过代数将概念相加,这就是本文的内容。我喜欢称它为**“思想的代数”**,因为我们正在使用基础数学将概念相加,形成复合概念。
让我们来谈谈想法,并把它们变成数字。回到这篇卡通人工智能文章,我们看到了文本块如何被转化为特殊的固定长度的数字列表(嵌入向量),这些向量的聚类揭示了关于漫画组主题的信息。单个单词也可以嵌入到向量中,通过它们与其他单词的关系给出它们的意思。传统的例子是向量“国王”——“男人”+“女人”=“女王”。这意味着我们可以学习一种表示法,这样国王、男人和女人的向量可以用来找到王后的向量。这是文字上的代数,但我们可以做得更多!事实证明你也可以用图片来做这件事!
Interpolating between yound and old faces (between faces, not ages). More on how we did this below.
通过为一组图像找到好的编码(即嵌入),你可以开始做我们在文字上做的那种“特征数学”,以酷的方式修改图片。以面部为例,模型可以被训练成包括诸如年龄、性别或面部是否戴眼镜之类的特征。然而,用于模拟人脸的神经网络(例如,autoencoder 或 gan)不同于用于模拟单词及其相互关系的数学类型(单词嵌入有 word2vec 、 gloVe 、 fastText 、 ELMo 、 BERT 、 XLNet 等)。).
文献中激励人心的例子
将这种概念代数应用于图像有许多实际应用。从 snapchat 的性别交换过滤器(T1)到带有图像特征提取的面部匹配(T2)都有使用案例。这种技术的一个著名例子是 thispersondoesnotexist.com 的,它可以生成看起来很真实的高分辨率人脸照片。
我研究这个主题的动机是几年前亚历克·拉德福德在论文“深度卷积生成对抗网络的无监督表示学习”中所做的一些工作。打开 PDF,看看第 10 页。这是我脑子里的灯真正打开的地方。他们展示了如何通过将想法加在一起,使用神经网络有条件地生成人脸。我们在第 10 页看到,“戴眼镜的男人”减去“不戴眼镜的男人”加上“不戴眼镜的女人”的向量给了我们一张戴眼镜的女人的脸。这种做法很疯狂。这些加号和减号促使我不断了解这方面的进展。就是超级爽。
我稍后将讨论我们为本文构建的内容。首先更多的是关于已经存在的。最新和最伟大的作品是我现在想谈论的。看看whichfaceisreal.com看看你是否能检测出真实与脸部的照片。诀窍是密切注意图像的背景。虽然假图像超级逼真,但它们没有经过良好的训练来生成逼真的背景。生成器确实理解像眼镜的光学、反射、阴影等东西,但是它有时填充背景很差。
下面以视频的形式嵌入了三个即将推出的令人惊叹的项目。在第一个“基于文本的视频编辑”中,我们看到视频编辑将被大规模破坏,因为我们现在可以使用深度学习来编辑人们在视频中说的话。激励人心的例子是一份关于股票市场的报告。在第二个视频中,你看到“现实神经说话头部模型的少镜头对抗学习”,其中模型学习拍摄几张图片(帧),并制作一个可以说话的模型(说话头部模型)。这篇论文使用了很多漂亮的技巧,比如元学习和现实主义分数。最后,第三个视频是关于学习和生成人脸的。作者使用了自己的数据集。这类似于我们下面使用自动编码器的方法。我们在项目中采用了一个可变自动编码器(VAE),但这只是一个细节。他很好地解释了让模特学习低维人脸模型的概念。
Text-based Editing of Talking-head Video (SIGGRAPH 2019)
Few-Shot Adversarial Learning of Realistic Neural Talking Head Models
Face embedding with PCA
看了外面的样品,让我们做自己的事。
行动计划
对于本文,我们用于创建人脸嵌入的策略是通过训练卷积自动编码器。我们最终选定了一种可变自动编码器。它基本上是 zip 文件的机器学习版本:你拍摄一幅图像,将其压缩成少量数据,然后尝试使用这些数据来重新绘制原始图像。通过修改压缩数据,您可以调整模型重建图片的方式。该压缩数据成为图像嵌入。我们使用的代码是根据托马斯·德阿纳的优秀文章修改而来的。
我们使用了 UTKFace 数据集(参见本文),而不是像在原始文章中那样使用水果。它被许可用于研究,所以我们在这里,做研究。该数据集的主要优势在于,它包含正确裁剪的对齐人脸,并按种族、年龄和性别进行标记。
使用自动编码器,神经网络的输入和输出在训练期间是相同的。这基本上意味着神经网络需要学习压缩然后解压缩图像,在这样做的过程中,它学习了构成人脸的本质特征。如果它可以学习人脸,那么我们就可以使用训练好的模型来做一些很酷的事情,比如**生成新的人脸,在人脸之间变形,给人脸添加概念,比如添加眼镜,或者减去一个性别添加另一个,或者降低年龄,**等等。
你会在这篇文章中注意到,我们并没有试图通过贴标签来分类。相反,我们感兴趣的是对数据集中的数据(面孔)分布进行建模,并使用该模型用数学“做事情”。这是无监督学习中的想法:你从数据中学习,而不强迫模型学习数据的标签。
训练模型学习人脸
我和玛丽·凯特·麦克弗森一起做这个项目,我们的第一步是在人脸数据集上训练自动编码器。该结构与原始文章相比几乎没有变化,只是增加了历元的数量,因为这对 faces 数据集有更好的结果。我花了整整两天的时间,试图根据我过去在音频方面的工作编写自己的卷积自动编码器,但结果不如汤姆的 VAE。正如我们在下图中看到的,重建的脸看起来相当不错,尽管肯定还是有些模糊。稍后我们会看到锐化滤波器可以帮助解决这个问题。
Face reconstruction using variational autoencoder. The first row is the input and the second is the resulting output.
Another example of reconstructed faces.
And even more…
代数!对来自验证数据集的人脸进行实验
训练好模型后,是时候对模型没有训练识别的图片进行一些有趣的嵌入实验了。第一个实验是两张脸之间的简单插值。生成这个的等式只是平均两个人脸嵌入,每次迭代加权第二个脸多一点,第一个脸少一点。这得到了一些有趣的结果!
The CEO to CTO morph.
And the gif, for your viewing pleasure.
我们可以拉名人进来!
Me morphing into Keanu Reeves. Because awesome!
下面是一张从我到基努过渡的 gif。看到脸是如何变化的很有趣。小胡子长得很迷人。有些地方有点起伏不定,但这是个好的开始。
The Daniel Shapiro, PhD to Keanu morph as a gif.
这是下一个合乎逻辑的步骤:
Mathieu Lemay to Keanu morph.
我们不得不多做一些,只是为了好玩。
The Sam to Daniel Shapiro, PhD morph. The beard gets weird as it grows in from the outside in and the inside out.
下一个实验是研究面部年龄。这个问题的总方程式是:
“原始脸”——“原始脸年龄的平均脸”+“新年龄的平均脸”
所以要把一个 35 岁的人变成一个 5 岁的孩子,你会说:
I = 35 岁的形象
a = 35 岁人脸嵌入的平均向量
n =岁人脸嵌入的平均向量
当你将向量 i-a+n 推入自动编码器的解码器时,你会得到原始人脸的娃娃脸版本。
Baby face of me. It didn’t quite delete the beard.
这对于非常年轻的脸比非常老的脸更有效,因为图像的模糊性使得很难显示皱纹之类的东西。下图显示的是马修·勒梅变成了一个婴儿。
Mathieu Lemay morphing into a devil baby!
而这里是这个神奇过程的 gif:
Baby eyes do not look good on an adult’s face.
添加眼镜效果很好:
Adding glasses to Mathieu Lemay.
它对我也有效:
Adding glasses to me.
以下是山姆脸的变形,最大限度地增加了女性特征:
现在我们来谈谈如何生成全新的面孔。我们可以在嵌入空间中进行随机行走,得到这样的人脸:
这个模型似乎理解照明:
Animation of generated faces. You can see that the lighting is a concept the neural network seems to understand.
嵌入空间中附近的人脸看起来相似:
A smaller subset of the same pictures to show the similarity between images.
放大时,生成的面会非常细致:
使用后处理步骤来锐化我们的面部生成器的输出看起来更加真实!想象面孔的锐化版本(一个男性;一名女性):
Before
After
Before
After
其他数据集呢?
回想一下,我们写过一篇关于外服一代和的文章,Mary Kate MacPherson 想用她策划的数据集尝试这个模型。以下是在外服数据集上训练后的初步结果,动画特征之间的变形:
Waifu morph
一个生成的外服:
外服变形为 gif:
显示组成 gif 的每一帧:
放大 gif,让您更清楚地看到细节:
结论?
我们有更多的材料,可能会进入第二篇文章。我认为这篇文章的结论是,我们可以在图像空间中进行数学运算,并像处理想法一样处理图像,包括年龄修改、面部变形、年龄修改、添加眼镜等功能(正如我们在 DCGAN 论文中看到的那样)、添加女性特征、生成面部、学习照明等等。我们看到这个想法也适用于动画角色,并且输出的真实感可以用经典的图像处理技术来提高,比如锐化滤波器。在本文中,您了解了无监督学习在人脸数据集上的一些背景和应用。如果你喜欢这篇文章,那么看看我的其他一些文章,比如“如何为一个人工智能项目定价”和“如何聘请人工智能顾问”嘿,看看我们的简讯!
下次见!
——丹尼尔
Lemay.ai
丹尼尔@lemay.ai
色情图片的面部识别仍然是一个糟糕的主意
(Adapted from Markus Spiske on Unsplash)
这个人是否真的建造了他声称建造的东西并不重要。
发生了什么事?几周前,一名在德国的中国开发者声称,他开发了一个应用程序,通过与社交媒体上的图像进行交叉检查,使用面部识别技术来识别网络色情中的女性。他的说法最初在中国微博平台微博上疯传,在评论帖子中引起了极大的兴奋。耶鲁大学法学院保罗·蔡中国中心的副研究员秦怡·傅将这个故事上传到推特上,并翻译给国际观众:
一名在德国工作的中国程序员表示,他和一些朋友已经识别出了来自世界各地的 10 万名色情女演员,将色情视频中的面孔与社交媒体上的照片进行交叉引用。目的是帮助其他人检查他们的女朋友是否曾出演过这些电影。‘’
她进一步报告说,根据最初的微博帖子,来自 PornHub 和 xVideos 等色情网站的超过 100 万亿字节的视频数据与来自脸书、Instagram、抖音、微博和其他社交媒体平台的个人资料图片进行了匹配,以确定 10 万名色情演员。
虽然没有人真正见过这款应用,自称为徐莉的开发者的身份也未得到证实,但这个故事还是流传开来。毕竟是关于色情的。
(Screenshot: Yiqin Fu on Twitter)
在微博上,徐莉向主板证实,他计划在接下来的一周发布一个“数据库模式”和进一步的“技术细节”。这从来没有发生过,因为他取消了他要回答媒体问题的直播。他删除了整个项目及其数据,这更可能是因为这个故事在网上引起的愤怒,而不是出于反省和更好的判断。
社交媒体上关于这个故事的许多讨论都围绕着这个应用程序是否曾经存在和工作过,这个团队在创建它的时候可能看过多少色情内容,以及他们是否有女朋友。不用说——我希望——这个角度分散了人们对将面部识别软件应用于色情内容的关注。
这个故事只是更广泛趋势的一个例子
有人会开发这样一款应用,这既不是不可能的,也不是不可能的。当我们根本不应该感到惊讶的时候,询问他们是否真的开发了这个应用程序,以及它可能或可能没有多准确地工作是假装错误的怀疑。
Motherboard 两年前就警告过这种用例,当时 Pornhub 和 xHamster 都开始使用面部识别技术来自动标记和分类他们的视频。他们称之为“等待发生的隐私噩梦”
还记得当 FindFace 被用于 dox 性工作者和色情演员,通过将他们的脸与俄罗斯社交媒体平台 VK 上的个人资料进行匹配?
这个想法既不是新的,也不是原创的。
不是每个人都生活在强有力的数据保护立法之下
徐莉最初的印象是,他的项目不存在法律问题,因为他没有公开数据,而且性工作在德国是合法的。后者是正确的,但与个人(更糟糕的是,生物特征)数据的非自愿收集和处理没有什么关系。
GDPR 定义了许多处理个人数据的许可理由。在这种情况下,显然不存在同意,也不存在任何合同义务或公共利益。当唯一的兴趣是欺骗女性,以便男性可以验证他们的妻子和女朋友是否曾出现在色情作品中时,以合法利益为由立案似乎很困难。
在处理照片时,照片被视为特别敏感的生物特征数据
“通过特定的技术手段对自然人进行独特的识别或认证.”
对于那些生活在欧盟和其他保护公民免受这种剥削性数据使用的国家的人来说,这是一个好消息。但是色情和社交媒体平台吸引了全球用户。到目前为止,并不是每个地区都受到强有力的数据保护,而且执行现有的法规通常既耗时又昂贵。
在色情作品的合法性受到限制或不确定的国家,对表演者的影响尤其成问题,在色情作品中表演是非常可耻的,数据保护立法要么不存在,要么不够强大,无法保护这种特殊的使用情况。
性别监控是一个越来越大的威胁
最终,这个项目是关于识别、揭露、羞辱或骚扰从事色情表演的女性。根据 twitter 上的翻译,该应用程序的最初目的是为男性提供一种方法来验证他们的妻子和女朋友是否曾出现在色情作品中。
“目的是帮助其他人检查他们的女朋友是否曾出演过这些电影。”
许多表演者使用艺名,这对于业余表演者和其他需要将自己的演艺事业与个人生活、家庭或其他职业分开的人来说尤为重要。通过面部识别的方式对他们进行 Doxxing 不仅是令人难以置信的侵犯和严重的隐私侵犯,也是性别化的监视。
而且也不是孤立事件。有针对性的在线骚扰既不是始于也不是止于 Gamergate,而是影响妇女和边缘化群体在网上表达自己的自由。所谓的 stalkerware 也是一种增长趋势。也就是说,软件直接或间接销售给虐待者,用于秘密监视配偶或前伴侣。对于任何想了解更多的人来说,Motherboard 在这个话题上有一个优秀的系列。
当前关于 deepfakes 的争论是另一个恰当的例子。大多数关于 deepfakes 的研究和报道都集中在使用的方法和 T2 的政治含义上。诚然,这些都是重要的问题。但值得记住的是, deepfake 视频技术的第一个主流用途是将女性的面孔插入色情视频中,以骚扰和羞辱她们。
早在当代监控技术出现之前,女性的身体、参与、运动和自主权就已经受到男性和社会的监视和控制。但是,随着数据量的不断增加和机器学习的进步,性别化的监督出现了更多的方式并被放大,正如监督的学者多年来一直指出的那样。
最后,使用类似的算法“永远”也不是解决办法
在对他的项目的强烈反对声中,开发者后来补充道,这款应用也可以被女性用来识别复仇色情。虽然我怀疑这种事后的想法是出于对复仇色情受害者的真正关心,但这在技术上是真的。一种能够通过社交媒体识别色情演员的算法也将发现那些从一开始就不想参与色情活动的女性的档案。
然而,在实践中,风险大于潜在收益。发现复仇色情很少是主要问题。受害者更为挣扎的是从各自的平台上删除图像,在他们的家庭地址和电话号码被公布后的个人安全,通常随之而来的骚扰浪潮的创伤,执法部门的不重视,以及处理这种事件对家人、朋友和工作场所的影响。面部识别对此没有任何帮助。
保护数据,保护隐私,安全分享裸照,是对复仇色情最好的防御。教授和学习如何做这些事情比试图用面部识别软件修复复仇色情片更能利用任何人的时间。
Nicole Shephard 是一名独立研究员、顾问和自由撰稿人,她拥有性别研究博士学位和十年的技术和人力资源工作经验。她写了关于数据和技术的性别政治,以及工作场所的多元化包容。
碎碎念: @kilolo_
如何建立一个微笑探测器
Photo by Nathan Dumlao on Unsplash
使用您自己的网络摄像头,并使用 OpenCV 检测快乐
用 Python 建立一个面部识别模型
1.介绍
企业努力提供最重要的产品:幸福。
为什么?快乐可能不仅仅是一种化学反应。一个快乐的顾客更有可能再次走进这扇门,关于快乐的数据可以帮助企业了解哪些产品会做得更好,并具有更高的保留率。机器可以学习识别快乐,在本教程中,我将向你展示如何创建一个可以识别快乐的面部识别模型。
让我们从基础开始。我们如何表达快乐?在脸上,大多是通过我们的眼神和笑容。当我们看到某人脸上的微笑时,我们会自然而然地推断出他是快乐的。微笑可以有很多变化,但它的形状大多类似于一种扁平的“U”形。
😃
在接下来的步骤中,我将放下我的代码,解释我是如何构建幸福探测器的。要理解算法背后的直觉,请查看之前关于 Viola-Jones 算法的文章。
你需要什么来建造幸福探测器:
- 蟒蛇导航员:https://docs.anaconda.com/anaconda/navigator/
- OpenCV:https://opencv.org/
- 哈尔瀑布(链接如下)
我在 Anaconda 上使用 Spyder,但是我想你也可以使用 Jupyter Nb。一旦你拥有了一切,就进入 IDE(代码编辑器)。
如果你使用 spyder,你应该有一个这样的屏幕。
在开始编码之前,请确保下载了 haar cascades(下一节)
2.哈尔瀑布
正如我在上面引用的文章中解释的那样,Viola-Jones 算法使用类似 haar 的特征来检测面部属性。级联是一系列过滤器,它们将一个接一个地应用,以通过面部特征来检测面部。
这些过滤器存储在 Haar Cascade GitHub 存储库中它们自己的 XML 文件中。
为了构建我们的幸福探测器,我们需要这 3 个 XML 文件:
-haarcascade _ eye . XML
-haarcascade _ smile . XML
-haarcascade _ frontal face _ default . XML
这些是脸部、眼睛和微笑的层叠。如果我们的形象是一张快乐的脸,我们必须具备这些特征。从每个链接中获取代码,将其放入文本编辑器中,并根据上面提到的名称保存文件。将所有三个 XML 文件放在同一个文件夹中,在这里您将启动一个 python 笔记本。现在我们有了 Haar Cascades,让我们来看看虚拟环境。
— — — — — — —现在,终于到了开始编码的时候了。— — — — — — — —
3.导入 OpenCV 并加载级联
import cv2cascade_face = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')cascade_eye = cv2.CascadeClassifier('haarcascade_eye.xml') cascade_smile = cv2.CascadeClassifier('haarcascade_smile.xml')
唯一需要导入的库是 OpenCV。尽管它是一个非常强大的对象识别工具,但它不是最强大的。有更新更好的,但 OpenCV 仍然提供了很多价值,是理解对象识别基础的好方法。
导入 OpenCV (cv2)后,我调用了下载的每个级联。为此,我只需要使用名为 CascadeClassifier 的 OpenCV 函数。
4.定义面部、眼睛和微笑的检测功能
def detection(grayscale, img):
face = cascade_face.detectMultiScale(grayscale, 1.3, 5)
for (x_face, y_face, w_face, h_face) in face:
cv2.rectangle(img, (x_face, y_face), (x_face+w_face, y_face+h_face), (255, 130, 0), 2)
在为检测定义函数之前,您应该知道它将应用于单个图像,然后将这些图像放在一起以获得最终结果。
由于 Viola-Jones 算法适用于灰度图像,因此我将该函数的第一个参数输入为灰度。但是,我也希望最终输出的原始图像是彩色的,所以我为原始图像输入了另一个参数 img 。
接下来,我需要得到检测人脸的矩形的坐标。为了定义这些坐标,我取了 4 个元组:x,y,w,h.
x & y 是左上角的坐标,w & h 分别是矩形的宽度和高度。我将这些元组存储在变量 face 中,然后使用 OpenCV 的另一个函数detect scale来实际获取这些坐标。因此,我们使用我们的对象 Cascade_face 并将 detectMultiScale 方法应用于它,该方法具有 3 个参数:
-在 b & w
中分析图像时的灰度-比例因子 1.3(图像的大小将减小 1.3 倍)
-可接受的最小邻居区域数:5 个邻居。
接下来,为了实际绘制矩形,我创建了一个“for 循环”,在 faces 中有 4 个元组 x_face,y_face,h_face,w_face。在 for 循环中,我使用了 rectangle 函数,这是另一个 OpenCV 函数。这实际上在你的脸上画出了矩形,并给出了以下参数:
-‘img’因为我们希望矩形画在我们原始的彩色图像上。
-左上角坐标:x 和 y
-右下角坐标:w 和 h
-矩形颜色:我选择了偏蓝的颜色。
-矩形边缘的厚度:我选了 2。(这并不重要,但 2 是个不错的选择)
注意:下面的代码是续篇(我已经把它们都放在底部了)
ri_grayscale = grayscale[y_face:y_face+h_face, x_face:x_face + w_face]
ri_color = img[y_face:y_face+h_face, x_face:x_face+w_face]
现在我完成了面部,我要检测眼睛。这是唯一有点棘手和难以理解的部分。基本上,上面的几行提到你在面部寻找眼睛,因此面部成为我们的“感兴趣区域”。因为该算法适用于灰度图像,所以我们输入灰度的参数。但是,我也想得到彩色图像,所以我将为彩色图像添加另一个参数。
本质上,有两个感兴趣的区域:一个用于灰度图像,一个用于原始彩色图像。随后,我用矩形 y:y+h 和 x:x+w 的坐标范围在灰度图像上创建ri _ gray,然后,在彩色图像上,我用相同的坐标为矩形创建 ri_color 。
eye = cascade_eye.detectMultiScale(ri_grayscale, 1.2, 18)
for (x_eye, y_eye, w_eye, h_eye) in eye:
cv2.rectangle(ri_color,(x_eye, y_eye),(x_eye+w_eye, y_eye+h_eye), (0, 180, 60), 2)
对于眼睛,我对面部重复了第一步,用 cascase_eye 创建了一个名为 eye 的对象,并使用detect scale方法找到 4 个元组。我将比例因子改为 1.2,最小邻居数改为 18。(我不得不试验这些值以获得完美的结果)。
接下来,我也为眼睛创建了一个 for 循环。你可以复制粘贴前一个,只需要改变元组名称,并选择不同的颜色。
smile = cascade_smile.detectMultiScale(ri_grayscale, 1.7, 20)
for (x_smile, y_smile, w_smile, h_smile) in smile:
cv2.rectangle(ri_color,(x_smile, y_smile),(x_smile+w_smile, y_smile+h_smile), (255, 0, 130), 2)
return img
然后再次为微笑,我重复同样的步骤,我做了脸和眼睛。当使用检测多尺度方法时,我使用了 1.7 的比例因子和 20 的最小邻居。(就像我之前说的,这需要一些实验来弄清楚)。完成所有操作后,您只需返回原始框架。
5.在网络摄像头上显示输出
vc = cv2.VideoCapture(0)while True:
_, img = vc.read()
grayscale = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
final = detection(grayscale, img)
cv2.imshow('Video', final)
if cv2.waitKey(1) & 0xFF == ord('q'):
break vc.release()
cv2.destroyAllWindows()
我首先创建了一个名为 vc 的对象,并使用了来自 OpenCV 的视频捕获类。它只需要一个参数:0 或 1。如果您使用内置网络摄像头,则为 0;如果是外置网络摄像头,则为 1。
由于检测函数只对单个图像起作用,我现在必须创建某种循环来允许它对一系列图像进行检测。所以我开始了一个无限的 while 循环,稍后我将使用一个 break 函数来中断它。接下来的几行可能看起来有些混乱,但是基本上,VideoCapture 的 read 方法得到了两个元素:其中一个是来自网络摄像头的最后一帧。因为你只想要那个,所以使用 _,img ,因为 read 方法返回 2 帧,而你只想要这个。现在我需要从视频捕获类中调用这个方法。
我使用 cvtColor 函数将彩色 img 转换为灰度,因为它需要一个 b & w 帧用于检测功能。我称之为灰度,取 cvtColor 类,取 2 个参数:
-帧(img)
- cv2。COLOR _ BGR2GRAY 对蓝绿色红色进行平均,以获得正确的灰色阴影。
现在我创建了一个名为’ final 的新变量,这将是 detect 函数的结果。在’ final '中,我使用带有参数灰度和 img 的检测函数。
imshow 函数是另一个 OpenCV 函数,它允许我们用矩形动画查看来自网络摄像头的原始素材。它以动画方式显示处理过的图像。
然后我用另一个函数在我用完的时候关闭窗口。我应用了一个 if 条件,每当我按下键盘上的‘q’按钮时,它就会终止应用程序。这将中断 while 循环以结束该过程。
最后,我用释放的方法关闭摄像头,用 DestroyAllWindows 函数终止窗口。
我把我的完整代码粘贴在下面,以防上面的摘录有任何混淆。
6.结果
起初,有很多原因导致这个模型不起作用。它会把一张没有微笑的脸也检测为微笑。为了得到完美的结果,我不得不使用比例因子和最少的邻居。
使用 React 和 face-api.js 的 BNK48 偶像团体面部识别 SPA
如何零后端代码部署自己的偶像识别系统
如今,人脸检测和识别已经不是什么新鲜事了。一年前,我曾经尝试在 Python 上使用 TensorFlow 和 facenet 制作自己的面部识别系统。该项目旨在从 AKB48 成员的照片中进行人脸检测和识别。
通过看他们的脸,你可能会同意我的观点,任何人都很难记住他们的名字,更不用说识别和区分他们的脸了。(声明一下,我不是粉丝什么的)
我的 Python 项目进行得非常好。我可以在 Jupyter-Notebook 中编写代码,从任何输入图像中检测和识别成员。然而,该系统是在 Python 环境下运行的,这对于大多数人脸识别系统来说是很正常的。但这意味着,如果我想从其他设备输入图像,例如智能手机,我需要创建前端来连接和发送图像到 Python 后端,以处理面部检测和识别。(大量工作要做)
由于缺乏动力,项目被搁置,一年过去了。直到我找到了文森特·穆勒的 face-api.js ,一个用 TensorFlow.js 进行人脸检测和识别的 JavaScript API。现在可以在浏览器上运行深度学习的所有过程,而不需要后端代码。(声音好听!)
是的,不需要任何后端编码或环境设置!!你需要的只是一个静态的虚拟主机。它可以在任何设备或浏览器上运行。(如果你的浏览器可以运行 TensorFlow.js,就这些)在本文的最后,我会向你展示如何将这个 React app 部署到 Github 页面。
又一个偶像团体:BNK48
这次回到我的项目,我决定使用另一个偶像团体的照片, BNK48 ,AKB48 的姐妹乐队,总部设在曼谷。
我来告诉你为什么偶像组合是实验面部识别 app 的好选择。(再次声明,我不是粉丝。)这是因为我们将需要许多已知的面孔和名字来测试我们的系统,对吗?使用 5-10 张照片来测试系统可能很容易,但在现实世界中,你不会为仅仅 10 个人制作人脸识别系统,对吗?这就是为什么偶像团体的 30-50 名成员是一个很好的测试数字。(不算少,也不算多)我们可以很容易地从网上找到他们各种视角的肖像照片,尤其是他们的 facebook 粉丝页面。
我们要做什么?
在这里,在这个项目中,我们将使用 React 和 face-api.js 库制作单页 App 来检测和识别偶像人脸。由于 Vincent 在他的 API 中为我们完成了所有困难的部分,该 API 带有预先训练的人脸检测、人脸标志、人脸对齐和人脸识别模型,所以我们不必自己训练模型。我们甚至也不需要用 TensorFlow 写 DL 模型。事实上,你真的不需要知道深度学习或 CNN 如何制作这个应用程序。你所需要知道的至少是 JavaScript 和 React 的基本概念。
如果你迫不及待想看看它的样子,请访问我的演示页面这里。而我的 App 完整回购就是这里。我们将在本教程中制作的代码将会更简单,但是不要担心,我也会在另一个 repo 中分享它。
人脸识别系统简介
如果您已经知道它是如何工作,或者不是很关心,您可以直接进入编码部分。
现在,让我们想象一下,当你去某个政府办公室索要一份你的个人文件时。柜台后面的工作人员通常会要求你证明你是谁。你给他们看你的身份证。她看着你的名字和照片,然后检查你的脸,确保你就是你声称的那个人。
同样,面部识别系统应该已经存储了您的姓名以及您的参考面部信息。然后,当你输入另一张照片进行识别时,系统将首先尝试检测图像上是否存在任何人脸,在这一步人脸检测网络将完成这项工作。我在这个项目中使用的模型是微型人脸检测器,因为它体积小,便于移动。(API 还为面部检测器提供了 SSD mobileNet 和 MTCNN ,但现在让我们忘记它们。)
回到我们的系统。一旦检测到人脸,人脸检测器模型将返回每个人脸的边界框,告诉我们人脸在图像中的位置。然后,我们使用人脸标志网络来标记 68 个点的人脸标志,并在馈送到人脸识别网络之前使用对齐模型来确保人脸居中。
人脸识别网络是另一个神经网络(准确地说, RestNet-34 类似于神经网络)返回一个人脸描述符(特征向量包含 128 个值),我们可以用它来比较和识别图像中的人。
就像指纹一样,人脸描述符是每张人脸的唯一值。当我们比较来自不同图像源的同一个人的面部描述符时,它们应该非常接近。在这个项目中我们用欧几里德距离来比较。如果距离小于我们设置的阈值,我们确定他们很可能是同一个人。(距离越小,自信越高)
通常情况下,系统会将每个人的脸部描述符作为参考,同时将他或她的名字作为标签保存。当我们输入查询图像时,系统会将新图像的人脸描述符与所有参考描述符进行比较,并识别出最低的人。如果比较结果都不低于阈值,此人将被识别为未知。
开始编码吧!
有 2 个功能,我们希望在这个应用程序中实现。一种是从输入图像文件中识别偶像,另一种是使用实况视频作为输入。
先从create-react-app
开始,安装react-router-dom
,启动 App。
npx create-react-app react-face-recognition
cd react-face-recognition
npm i react-router-domnpm start
打开浏览器,进入 http://localhost:3000/ 如果你看到带有 React 标志的起始页,那么你就可以继续了。现在用你喜欢的任何代码编辑器打开项目文件夹。您应该会看到这样的文件夹结构。
react-face-recognition
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
└── src
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
└── serviceWorker.js
现在转到src/App.js
,用下面的代码替换代码。
src/App.js
我们这里只有导入Home
组件并创建一条到"/"
的路径作为我们的登陆页面。我们将很快创建这个组件。
让我们从创建新文件夹src/views
开始,在这个文件夹中创建新文件Home.js
。然后将下面的代码放到文件中并保存。
src/views/Home.js
我们只创建 2 个链接,分别是Photo Input
链接到"localhost:3000/photo"
和Video Camera
链接到"localhost:3000/camera
。如果一切顺利,我们应该在登录页面看到如下内容。
Landing Page
Face API
在我们继续创建新页面之前,我们希望安装 face-api.js 并创建我们的 api 文件来连接与 API 的反应。现在回到控制台并安装库。
npm i face-api.js
该库附带 TensorFlow.js 和我们想要的所有组件,除了模型权重。如果你不知道它们是什么,模型权重是已经用大型数据集训练过的神经网络权重,在这种情况下,是成千上万的人脸图像。
由于许多聪明人已经为我们训练了模型,我们需要做的只是掌握我们想要使用的必要权重,并手动输入我们的项目。
你会在这里找到这个 API 的所有权重。现在让我们新建一个文件夹public/models
来放置所有的模型权重。然后下载所有必要的重量到下面的文件夹。(正如我告诉你的,我们将在这个项目中使用微型人脸检测器型号,所以我们不需要 SSD MobileNet 和 MTCNN 型号。)
Necessary Models
确保您将所有重量放在下面的public/models
文件夹中,否则没有合适的重量,我们的模型将无法工作。
react-face-recognition
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│ ├── models
│ │ ├── face_landmark_68_tiny_model-shard1
│ │ ├── face_landmark_68_tiny_model-weights_manifest.json
│ │ ├── face_recognition_model-shard1
│ │ ├── face_recognition_model-shard2
│ │ ├── face_recognition_model-weights_manifest.json
│ │ ├── tiny_face_detector_model-shard1
│ │ └── tiny_face_detector_model-weights_manifest.json
│ ├── favicon.ico
│ ├── index.html
│ └── manifest.json
现在返回并为 API 创建新文件夹src/api
,并在文件夹内创建新文件face.js
。我们要做的是加载模型并创建函数来将图像提供给 API 并返回所有的人脸描述,还可以比较描述符来识别人脸。稍后我们将导出这些函数并在 React 组件中使用。
src/api/face.js
这个 API 文件有两个重要的部分。第一个是用函数loadModels()
加载模型和权重。我们在这一步只加载微小人脸检测器模型、人脸标志微小模型和人脸识别模型。
另一部分是函数getFullFaceDescription()
,其接收图像斑点作为输入,并返回全脸描述。该函数使用 API 函数faceapi.fetchImage()
将图像 blob 提取到 API。然后faceapi.detectAllFaces()
将获取该图像并找到图像中的所有人脸,然后.withFaceLandmarks()
将绘制 68 个人脸标志,然后使用.withFaceDescriptors()
返回 128 个值的人脸特征作为Float32Array
。
值得一提的是,我使用 image inputSize
512 像素进行图像输入,稍后将使用 160 像素进行视频输入。这是 API 推荐的。
现在我要你把下面的图片保存到新文件夹src/img
中,并命名为test.jpg
。这将是我们的测试图像来测试我们的应用程序。(以防你不知道,她是 Cherprang,顺便说一下,是 BNK48 的成员。)
Save this image as src/img/test.jpg
让我们创建新文件src/views/ImageInput.js
。这将是视图组件输入和显示我们的图像文件。
src/views/ImageInput.js
此时,该组件将只显示测试图像src/img/test.jpg
,并开始将 API 模型加载到您的浏览器中,这将花费几秒钟的时间。之后,图像将被输入 API 以获得完整的面部描述。我们可以将返回的fullDesc
存储在state
中以备后用,也可以在 console.log 中看到它的详细信息
但是在此之前,我们必须将ImageInput
组件导入到我们的src/App.js
文件中。并为/photo
创建新的Route
。开始了。
src/App.js with new Route and Component
现在,如果您转到登录页面[http://localhost:3000](http://localhost:3000)
并点击Photo Input
,您应该会看到照片显示。如果你检查你的浏览器控制台,你应该看到这个图片的全脸描述如下。
面部检测盒
如你所见,描述包含了我们在这个项目中需要的所有人脸信息,包括descriptor
和detection
。detection
内有坐标x``y``top``bottom``left``right``height``width
等方框信息。
face-api.js 库自带函数用 html 画布画人脸检测框,真的很好看。但是既然我们用的是 React,为什么不用 CSS 来画人脸检测框呢,这样我们就可以用 React 的方式来管理框和识别显示了。
我们想要做的是使用检测的box
信息在图像上叠加人脸框。我们还可以稍后显示应用程序识别的每张脸的名称。这就是我如何将drawBox
添加到ImageInput
组件中。
让我们一起添加input
标签,这样我们就可以改变输入图像。
src/views/ImageInput.js
在 React 中使用内联 CSS,我们可以像这样放置所有的面部框来覆盖图像。如果您尝试用更多面孔来更改照片,您也将能够看到更多框。
面部识别
现在有趣的部分来了。为了识别一个人,我们需要至少一个参考图像来从图像中提取 128 个特征向量值或descriptor
。
API 具有函数LabeledFaceDescriptors
来为我们想要识别每个人创建描述符和名字的标签。该标签将与查询的descriptor
一起提供给 API 以匹配人员。但在此之前,我们需要准备一个名称和描述符的配置文件。
平面轮廓
我们已经有一个 Cherprang 的图像参考。因此,让我们使用它的descriptor
来制作一个配置文件。
我们现在要做的是创建新的 JSON 文件和文件夹src/descriptors/bnk48.json
。该文件将包含成员姓名和参考照片中的描述符。这是第一个只有一个descriptor
的样本文件。
Sample face profile
如果我们有所有成员的照片,我们可以添加descriptor
并逐一命名来完成我们的面部轮廓。你知道吗?我已经做了一个。我用每个成员的 5-10 张照片来创建这个完整的面部轮廓。所以,你可以下载这个文件并替换src/descriptors/bnk48.json
,很简单。(抱歉,我用泰语和平假名作为显示名称)
整个成员的文件大小在 1MB 左右,对于我们的测试 App 来说还不错。但是在现实世界中,您可能需要将所有面部轮廓存储在数据库中,这样您就不必再担心文件大小,但是您将需要使用服务器端来运行面部识别过程。
面部匹配器
下一步我们要为人脸识别任务创建labeledDescriptors
和faceMatcher
。现在回到src/api/face.js
,然后将下面的函数添加到你的文件中。
src/api/face.js add function createMatcher
该函数将接收面部轮廓(JSON 文件)作为输入,并创建每个成员的描述符的labeledDescriptors
,以其名称作为标签。然后我们可以创建并导出带标签的faceMatcher
。
你可能会注意到我们配置了maxDescriptorDistance
0.5。这是欧氏距离的阈值,用来确定引用描述符和查询描述符是否足够接近,可以说点什么。API 默认值为 0.6,对于一般情况来说已经足够了。但我发现 0.5 对我来说更精确,误差更小,因为一些偶像的脸非常相似。如何调整这个参数取决于您。
既然我们的函数已经准备好了,让我们回到src/views/ImageInput.js
来完成我们的代码。这是我们的最后一个。
Final code for ImageInput.js
在这个最终代码中,我们从face.js
导入createMatcher
函数,并用我们准备好的面部轮廓创建faceMatcher
。内部函数handleImage()
,我们从图像中得到fullDesc
后,绘制出descriptors
,找到每张脸的最佳match
。
然后,我们使用p
标签和 CSS 在每个人脸检测框下显示我们的最佳匹配。就像这样。
Face detect and recognize correctly
如果您已经下载了完整的面部轮廓。你可以试着用这个改变形象。我希望你能看到正确匹配检测到的所有人脸!!
Try this image
实时视频输入
本节将指导您使用 React-webcam 将实时视频作为 face-api.js 的输入。让我们从安装库开始。
npm i react-webcam
同样,在制作新的视图组件之前,我们还需要在/src/App.js
中添加一个用于视频输入的Route
。我们将很快创建VideoInput
组件。
Add VideoInput Component and Route
视频输入组件
让我们创建新文件src/views/VideoInput.js
并将下面的所有代码放入文件并保存。这是这个组件的完整代码。(不再按部就班。解释如下。)
人脸检测和识别的所有机制与ImageInput
组件相同,除了输入是每隔 1500 毫秒从网络摄像头捕获的屏幕截图。
我将屏幕尺寸设置为 420x420 像素,但您可以尝试更小或更大的尺寸。(尺寸越大,处理人脸检测所需的时间越长)
内部功能setInputDevice
我只是检查设备是否有 1 个或 2 个摄像头(或更多)。如果只有一个摄像头,我们的应用程序将假设它是一台 PC,然后我们将从网络摄像头facingMode: user
捕捉,但如果有两个或更多,那么它可能是智能手机,然后我们将从背面用摄像头捕捉facingMode: { exact: ‘environment’ }
我使用与组件ImageInput
相同的函数来绘制人脸检测框。其实我们可以把它做成另一个组件,这样就不用重复两遍了。
现在我们的应用程序完成了。你可以用你的脸测试 VideoInput,但它很可能会把你识别为unknown
或者有时会错误地把你识别为某个偶像。这是因为如果欧几里德距离小于 0.5,系统将尝试识别所有的脸。
结论和经验教训
该应用程序可以相当准确地检测和识别偶像的脸,但仍有一些错误时有发生。这是因为拍摄对象可能没有直接面对相机,他们的脸可能会倾斜,或者照片被其他一些应用程序编辑过。
一些偶像可能长得很像,这让 App 有时会混淆。我发现,当来自不同的来源或光线设置时,偶像的脸会有所不同。戴眼镜或浓妆艳抹的偶像也会让我们的应用程序感到困惑。
我不得不承认这个系统并不完美,但仍有改进的余地。
我在 Chrome 和 Safari 上进行了测试,在 PC 上运行良好。我认为它应该也能在 IE 或 Firefox 上运行。用 Android 智能手机测试图像输入和视频输入都很好,但 react-webcam 由于安全问题不能与 iPhone 一起工作,我仍在寻找解决方法。老式手机往往无法与 TensorFlow 一起正常工作,因为它需要足够的计算能力来运行神经网络。
部署到 Github 页面
您可以将这个应用程序部署到任何静态主机,但是本节将指导您使用一些技巧将这个 React 应用程序部署到 Github 页面。你需要有 Github 帐户。如果你没有,去做一个。它是免费的。
首先,让我们安装gh-pages
库。
npm i gh-pages
然后我们需要像这样在src/App.js
的createHistory()
里面加上{ basename: process.env.PUBLIC_URL }
。
现在转到您的 Github 并创建一个名为 App name 的新存储库,在我们的例子中是react-face-recognition
,然后复制 git URL,稍后添加到我们的项目中。接下来,打开package.json
,像这样用你的 Github 账号和 App 名添加"homepage"
。
"homepage": "http://YOUR_GITHUB_ACCOUNT.github.io/react-face-recognition"
暂时不要关闭package.json
文件,因为我们会像这样在"scripts"
下添加predeploy
和deploy
命令行。
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"predeploy": "npm run build",
"deploy": "gh-pages -d build"
}
现在您可以保存文件并返回到您的控制台终端,然后运行 git 命令将代码上传到您的 Github 存储库,并运行npm run deploy
部署到 Github 页面。该页面应使用您设置为http://YOUR_GITHUB_ACCOUNT.github.io/react-face-recognition
的 URL 发布
git add .
git commit -m "make something good"
git remote add origin https://github.com/YOUR_GITHUB_ACCOUNT/react-face-recognition.git
git push -u origin master
npm run deploy
你可以在这里查看本教程的 Github 页面,也可以完成回购。
我希望你喜欢我的教程,并尝试让你自己的反应面部识别。如果你觉得这个教程很简单,想看更完整的版本,请访问我的演示页面这里,还有回购。