TowardsDataScience 博客中文翻译 2020(一百八十二)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

用 Keras 构建多输出卷积神经网络

原文:https://towardsdatascience.com/building-a-multi-output-convolutional-neural-network-with-keras-ed24c7bc1178?source=collection_archive---------2-----------------------

在这篇文章中,我们将探索 Keras functional API,以建立一个多输出深度学习模型。我们将展示如何训练能够预测三种不同输出的单一模型。通过使用 UTK 人脸数据集,该数据集由超过 2 万张不受控制的环境中的人的照片组成,我们将预测数据集中呈现的每条记录的年龄、性别和性别,性别的准确率达到 91%,种族的准确率达到 78%。

数据集

UTKFace 数据集是一个大型数据集,由超过 2 万张人脸图像组成,并分别标注了年龄、性别和种族。图像被适当地裁剪到面部区域,但是在姿势、照明、分辨率等方面显示一些变化。

为了检索每个记录的注释,我们需要解析文件名。每条记录以如下格式存储:年龄 _ 性别 _ 种族 _ 日期&time.jpg

其中:

  • 年龄是从 0 到 116 的整数
  • 性别是一个整数,其中 0 代表男性,1 代表女性
  • 种族是一个从 0 到 4 的整数,分别代表白人、黑人、亚洲人、印度人和其他人种
  • 日期和时间,表示照片拍摄的时间

如果你想进一步了解这个数据集,请查看他们的网站

让我们首先导入一些库并创建我们的字典来帮助我们解析来自数据集的信息,以及一些其他信息(数据集位置、训练分割、样本的宽度和高度)。

import numpy as np 
import pandas as pd
import os
import glob
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as snsdataset_folder_name = 'UTKFace'TRAIN_TEST_SPLIT = 0.7
IM_WIDTH = IM_HEIGHT = 198dataset_dict = {
    'race_id': {
        0: 'white', 
        1: 'black', 
        2: 'asian', 
        3: 'indian', 
        4: 'others'
    },
    'gender_id': {
        0: 'male',
        1: 'female'
    }
}dataset_dict['gender_alias'] = dict((g, i) for i, g in dataset_dict['gender_id'].items())
dataset_dict['race_alias'] = dict((r, i) for i, r in dataset_dict['race_id'].items())

让我们定义一个函数来帮助我们从数据集中提取数据。该函数将用于迭代 UTK 数据集的每个文件,并返回一个包含我们记录的所有字段(年龄、性别和性别)的熊猫数据帧。

def parse_dataset(dataset_path, ext='jpg'):
    """
    Used to extract information about our dataset. It does iterate over all images and return a DataFrame with
    the data (age, gender and sex) of all files.
    """
    def parse_info_from_file(path):
        """
        Parse information from a single file
        """
        try:
            filename = os.path.split(path)[1]
            filename = os.path.splitext(filename)[0]
            age, gender, race, _ = filename.split('_') return int(age), dataset_dict['gender_id'][int(gender)], dataset_dict['race_id'][int(race)]
        except Exception as ex:
            return None, None, None

    files = glob.glob(os.path.join(dataset_path, "*.%s" % ext))

    records = []
    for file in files:
        info = parse_info_from_file(file)
        records.append(info)

    df = pd.DataFrame(records)
    df['file'] = files
    df.columns = ['age', 'gender', 'race', 'file']
    df = df.dropna()

    return dfdf = parse_dataset(dataset_folder_name)
df.head()

数据可视化

作为理解数据集分布以及模型生成的预测的重要步骤,建议对数据集执行一些数据可视化过程。我们将首先定义一个助手函数,根据给定的 Pandas 系列生成饼图:

import plotly.graph_objects as godef plot_distribution(pd_series):
    labels = pd_series.value_counts().index.tolist()
    counts = pd_series.value_counts().values.tolist()

    pie_plot = go.Pie(labels=labels, values=counts, hole=.3)
    fig = go.Figure(data=[pie_plot])
    fig.update_layout(title_text='Distribution for %s' % pd_series.name)

    fig.show()

种族分布

让我们首先用我们预定义的 plot_distribution 方法绘制比赛分布图。

plot_distribution(df['race'])

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

快速浏览一下这个图,我们可以看到几乎一半的样本来自白色人种,所以我们可以期待这个组有很大的准确性。其他种族,如黑人、印度人和亚洲人也显示了大量的样本,可能也使我们获得了准确的数据。另一方面,种族“其他人”(西班牙裔、拉丁裔等)显示少量样本,更可能具有较小的准确性。

性别分布

plot_distribution(df['gender'])

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

对于男性和女性样本,我们有相当好的平衡数量的记录,所以当使用我们的模型时,我们应该对两类都有很高的准确性。

年龄分布

我们还可以使用一个简单的直方图来绘制年龄特征在数据集上的分布,该直方图包含 20 个条块/扇区。

import plotly.express as px
fig = px.histogram(df, x="age", nbins=20)
fig.update_layout(title_text='Age distribution')
fig.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们也可以在饼图中显示同样的图。让我们将年龄列分组,然后用饼图绘制出来

bins = [0, 10, 20, 30, 40, 60, 80, np.inf]
names = ['<10', '10-20', '20-30', '30-40', '40-60', '60-80', '80+']age_binned = pd.cut(df['age'], bins, labels=names)
plot_distribution(age_binned)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以观察到,我们的数据集主要由年龄在 20 至 30 岁之间的个体组成,其次是 30 至 40 岁以及 40 至 60 岁的个体。这些群体约占我们数据集的 70%,因此我们可以假设我们在预测这些范围内的个体时会有很好的准确性。

我们还可以对我们的数据集执行一些多变量分析,但由于这篇文章的范围是演示 Keras 的多输出模型的用法,所以我们不会涉及它——如果你们感兴趣的话,也许改天吧。

数据生成程序

为了将我们的数据输入到我们的 Keras 多输出模型中,我们将创建一个辅助对象作为数据集的数据生成器。这将通过生成批量数据来完成,这些数据将用于向我们的多输出模型提供图像及其标签。这一步也要完成,而不是一次将所有数据集加载到内存中,这可能会导致内存不足错误。

from keras.utils import to_categorical
from PIL import Imageclass UtkFaceDataGenerator():
    """
    Data generator for the UTKFace dataset. This class should be used when training our Keras multi-output model.
    """
    def __init__(self, df):
        self.df = df

    def generate_split_indexes(self):
        p = np.random.permutation(len(self.df))
        train_up_to = int(len(self.df) * TRAIN_TEST_SPLIT)
        train_idx = p[:train_up_to]
        test_idx = p[train_up_to:] train_up_to = int(train_up_to * TRAIN_TEST_SPLIT)
        train_idx, valid_idx = train_idx[:train_up_to], train_idx[train_up_to:]

        # converts alias to id
        self.df['gender_id'] = self.df['gender'].map(lambda gender: dataset_dict['gender_alias'][gender])
        self.df['race_id'] = self.df['race'].map(lambda race: dataset_dict['race_alias'][race]) self.max_age = self.df['age'].max()

        return train_idx, valid_idx, test_idx

    def preprocess_image(self, img_path):
        """
        Used to perform some minor preprocessing on the image before inputting into the network.
        """
        im = Image.open(img_path)
        im = im.resize((IM_WIDTH, IM_HEIGHT))
        im = np.array(im) / 255.0

        return im

    def generate_images(self, image_idx, is_training, batch_size=16):
        """
        Used to generate a batch with images when training/testing/validating our Keras model.
        """

        # arrays to store our batched data
        images, ages, races, genders = [], [], [], []
        while True:
            for idx in image_idx:
                person = self.df.iloc[idx]

                age = person['age']
                race = person['race_id']
                gender = person['gender_id']
                file = person['file']

                im = self.preprocess_image(file)

                ages.append(age / self.max_age)
                races.append(to_categorical(race, len(dataset_dict['race_id'])))
                genders.append(to_categorical(gender, len(dataset_dict['gender_id'])))
                images.append(im)

                # yielding condition
                if len(images) >= batch_size:
                    yield np.array(images), [np.array(ages), np.array(races), np.array(genders)]
                    images, ages, races, genders = [], [], [], []

            if not is_training:
                break

data_generator = UtkFaceDataGenerator(df)
train_idx, valid_idx, test_idx = data_generator.generate_split_indexes() 

构建我们的模型

在这一步,我们将定义我们的多输出 Keras 模型。我们的模型将由三个主要分支组成,每个分支对应一个可用的特征:年龄、性别和种族。

我们的卷积层的默认结构基于一个带有 ReLU 激活的 Conv2D 层,然后是 BatchNormalization 层、MaxPooling 层,最后是 MaxPooling 层。这些层中的每一层之后是最终的致密层。对我们试图预测的每个输出重复这一步。

这些默认层是在make _ default _ hidden _ layers方法中定义的,它将在构建我们模型的每个分支时被重用。在下面的代码中,我们将定义负责创建多输出模型的类。

from keras.models import Model
from keras.layers.normalization import BatchNormalization
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.layers.core import Activation
from keras.layers.core import Dropout
from keras.layers.core import Lambda
from keras.layers.core import Dense
from keras.layers import Flatten
from keras.layers import Input
import tensorflow as tfclass UtkMultiOutputModel():
    """
    Used to generate our multi-output model. This CNN contains three branches, one for age, other for 
    sex and another for race. Each branch contains a sequence of Convolutional Layers that is defined
    on the make_default_hidden_layers method.
    """
    def make_default_hidden_layers(self, inputs):
        """
        Used to generate a default set of hidden layers. The structure used in this network is defined as:

        Conv2D -> BatchNormalization -> Pooling -> Dropout
        """
        x = Conv2D(16, (3, 3), padding="same")(inputs)
        x = Activation("relu")(x)
        x = BatchNormalization(axis=-1)(x)
        x = MaxPooling2D(pool_size=(3, 3))(x)
        x = Dropout(0.25)(x) x = Conv2D(32, (3, 3), padding="same")(x)
        x = Activation("relu")(x)
        x = BatchNormalization(axis=-1)(x)
        x = MaxPooling2D(pool_size=(2, 2))(x)
        x = Dropout(0.25)(x) x = Conv2D(32, (3, 3), padding="same")(x)
        x = Activation("relu")(x)
        x = BatchNormalization(axis=-1)(x)
        x = MaxPooling2D(pool_size=(2, 2))(x)
        x = Dropout(0.25)(x) return x def build_race_branch(self, inputs, num_races):
        """
        Used to build the race branch of our face recognition network.
        This branch is composed of three Conv -> BN -> Pool -> Dropout blocks, 
        followed by the Dense output layer.
        """
        x = self.make_default_hidden_layers(inputs) x = Flatten()(x)
        x = Dense(128)(x)
        x = Activation("relu")(x)
        x = BatchNormalization()(x)
        x = Dropout(0.5)(x)
        x = Dense(num_races)(x)
        x = Activation("softmax", name="race_output")(x) return x def build_gender_branch(self, inputs, num_genders=2):
        """
        Used to build the gender branch of our face recognition network.
        This branch is composed of three Conv -> BN -> Pool -> Dropout blocks, 
        followed by the Dense output layer.
        """
        x = Lambda(lambda c: tf.image.rgb_to_grayscale(c))(inputs) x = self.make_default_hidden_layers(inputs) x = Flatten()(x)
        x = Dense(128)(x)
        x = Activation("relu")(x)
        x = BatchNormalization()(x)
        x = Dropout(0.5)(x)
        x = Dense(num_genders)(x)
        x = Activation("sigmoid", name="gender_output")(x) return x def build_age_branch(self, inputs):   
        """
        Used to build the age branch of our face recognition network.
        This branch is composed of three Conv -> BN -> Pool -> Dropout blocks, 
        followed by the Dense output layer. """
        x = self.make_default_hidden_layers(inputs) x = Flatten()(x)
        x = Dense(128)(x)
        x = Activation("relu")(x)
        x = BatchNormalization()(x)
        x = Dropout(0.5)(x)
        x = Dense(1)(x)
        x = Activation("linear", name="age_output")(x) return x def assemble_full_model(self, width, height, num_races):
        """
        Used to assemble our multi-output model CNN.
        """
        input_shape = (height, width, 3) inputs = Input(shape=input_shape) age_branch = self.build_age_branch(inputs)
        race_branch = self.build_race_branch(inputs, num_races)
        gender_branch = self.build_gender_branch(inputs) model = Model(inputs=inputs,
                     outputs = [age_branch, race_branch, gender_branch],
                     name="face_net") return model

model = UtkMultiOutputModel().assemble_full_model(IM_WIDTH, IM_HEIGHT, num_races=len(dataset_dict['race_alias']))

让我们看一下我们的模型结构,以便更好地理解我们正在构建什么。从中我们可以看到,我们有一个单一的输入,在我们的情况下,是我们正在向 CNN 提供的图像,它确实分解为三个独立的分支,每个分支都有自己的卷积层,然后是各自的密集层。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

训练我们的模型

现在,一旦我们有了可以使用的数据和定义的模型架构,就该训练我们的多输出模型了。但是在进行这一步之前,我们需要编译我们的模型。对于这个任务,我们将使用 0.0004 的学习率和 Adam 优化器,但是您也可以随意尝试其他超参数。我们还将为每个特征使用自定义损失权重和自定义损失函数。在构建我们的优化器时,让我们使用一个基于学习率除以时期数的衰减,因此我们将在时期内慢慢降低我们的学习率。

from keras.optimizers import Adaminit_lr = 1e-4
epochs = 100opt = Adam(lr=init_lr, decay=init_lr / epochs)model.compile(optimizer=opt, 
              loss={
                  'age_output': 'mse', 
                  'race_output': 'categorical_crossentropy', 
                  'gender_output': 'binary_crossentropy'},
              loss_weights={
                  'age_output': 4., 
                  'race_output': 1.5, 
                  'gender_output': 0.1},
              metrics={
                  'age_output': 'mae', 
                  'race_output': 'accuracy',
                  'gender_output': 'accuracy'})

现在,让我们用有效集和训练集的批量大小 32 来训练我们的模型。我们将使用一个模型检查点回调,以便在每个时期结束时将模型保存在磁盘上。

from keras.callbacks import ModelCheckpointbatch_size = 32
valid_batch_size = 32
train_gen = data_generator.generate_images(train_idx, is_training=True, batch_size=batch_size)
valid_gen = data_generator.generate_images(valid_idx, is_training=True, batch_size=valid_batch_size)callbacks = [
    ModelCheckpoint("./model_checkpoint", monitor='val_loss')
]history = model.fit_generator(train_gen,
                    steps_per_epoch=len(train_idx)//batch_size,
                    epochs=epochs,
                    callbacks=callbacks,
                    validation_data=valid_gen,
                    validation_steps=len(valid_idx)//valid_batch_size)

训练完模型后,让我们更好地了解一下模型在各个时期的训练集和验证集上的表现:

比赛准确性

plt.clf()
fig = go.Figure()
fig.add_trace(go.Scatter(
                    y=history.history['race_output_acc'],
                    name='Train'))fig.add_trace(go.Scatter(
                    y=history.history['val_race_output_acc'],
                    name='Valid')) fig.update_layout(height=500, 
                  width=700,
                  title='Accuracy for race feature',
                  xaxis_title='Epoch',
                  yaxis_title='Accuracy')fig.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以看到,在第 50 个时期,我们的模型在验证集上稳定下来,仅在训练集上有所增加,准确率约为 80%。

性别准确性

plt.clf()fig = go.Figure()
fig.add_trace(go.Scatter(
                    y=history.history['gender_output_acc'],
                    name='Train'))fig.add_trace(go.Scatter(
                    y=history.history['val_gender_output_acc'],
                    name='Valid')) fig.update_layout(height=500, 
                  width=700,
                  title='Accuracy for gender feature',
                  xaxis_title='Epoch',
                  yaxis_title='Accuracy') fig.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

与种族特征类似,我们可以看到,我们的模型能够学习大多数模式,以在第 30 个时期正确预测给定个体的性别,准确率约为 90%。

年龄平均绝对误差

plt.clf()fig = go.Figure()
fig.add_trace(go.Scattergl(
                    y=history.history['age_output_mean_absolute_error'],
                    name='Train'))fig.add_trace(go.Scattergl(
                    y=history.history['val_age_output_mean_absolute_error'],
                    name='Valid')) fig.update_layout(height=500, 
                  width=700,
                  title='Mean Absolute Error for age feature',
                  xaxis_title='Epoch',
                  yaxis_title='Mean Absolute Error')fig.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在预测年龄特征的任务中,我们可以看到,我们的模型需要大约 60 个时期来适当地稳定其学习过程,平均绝对误差为 0.09。

总体损失

fig = go.Figure()
fig.add_trace(go.Scattergl(
                    y=history.history['loss'],
                    name='Train'))fig.add_trace(go.Scattergl(
                    y=history.history['val_loss'],
                    name='Valid')) fig.update_layout(height=500, 
                  width=700,
                  title='Overall loss',
                  xaxis_title='Epoch',
                  yaxis_title='Loss')fig.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以注意到,到了时期 50,我们的模型开始稳定,损失值大约为 1.4。在损失曲线中也有一个峰值,它确实出现在年龄特征的平均绝对误差中,这可以解释年龄特征的学习对总损失的影响。

在测试集上评估我们的模型

为了评估我们的模型在测试集上的表现,让我们使用我们的 UTK 数据生成器类,但是这次使用测试索引。然后,我们将从训练好的模型中调用 predict_generator 方法,该方法将输出测试集的预测。

test_batch_size = 128
test_generator = data_generator.generate_images(test_idx, is_training=False, batch_size=test_batch_size)
age_pred, race_pred, gender_pred = model.predict_generator(test_generator, 
                                                           steps=len(test_idx)//test_batch_size)

让我们在所有测试样本上再迭代一次,以便将它们的标签放在一个列表中。我们还将提取每个记录的 argmax ,以便检索顶级预测和基本事实。

test_generator = data_generator.generate_images(test_idx, is_training=False, batch_size=test_batch_size)
samples = 0
images, age_true, race_true, gender_true = [], [], [], []
for test_batch in test_generator:
    image = test_batch[0]
    labels = test_batch[1]

    images.extend(image)
    age_true.extend(labels[0])
    race_true.extend(labels[1])
    gender_true.extend(labels[2])

age_true = np.array(age_true)
race_true = np.array(race_true)
gender_true = np.array(gender_true)race_true, gender_true = race_true.argmax(axis=-1), gender_true.argmax(axis=-1)
race_pred, gender_pred = race_pred.argmax(axis=-1), gender_pred.argmax(axis=-1)age_true = age_true * data_generator.max_age
age_pred = age_pred * data_generator.max_age

最后,让我们打印测试集上每个特性的分类报告。

from sklearn.metrics import classification_reportcr_race = classification_report(race_true, race_pred, target_names=dataset_dict['race_alias'].keys())
print(cr_race)precision    recall  f1-score   support white       0.80      0.91      0.85      2994
       black       0.86      0.82      0.84      1327
       asian       0.86      0.79      0.83      1046
      indian       0.74      0.74      0.74      1171
      others       0.38      0.19      0.25       502 accuracy                           0.80      7040
   macro avg       0.73      0.69      0.70      7040
weighted avg       0.78      0.80      0.78      7040

从上面的报告中,我们可以看到,我们的模型在预测亚裔和黑人个体方面非常出色,准确率为 86%,其次是白人 80%,印度人 74%。“其他”种族的精确度仅为 38%,但我们需要考虑到,与其他群体相比,该群体由不同的种族和民族以及一些样本组成。这一分类任务的加权准确率为 78%,表明我们的分类器能够正确地学习模式以区分不同类型的种族。

cr_gender = classification_report(gender_true, gender_pred, target_names=dataset_dict['gender_alias'].keys())
print(cr_gender)**precision    recall  f1-score   support** **male       0.94      0.87      0.91      3735
      female       0.87      0.94      0.90      3305** **accuracy                           0.90      7040
   macro avg       0.90      0.91      0.90      7040
weighted avg       0.91      0.90      0.90      7040**

从这个报告中,我们可以注意到,我们的模型在预测给定个体的性别方面非常出色,对于这项任务,加权准确率为 91%。

from sklearn.metrics import r2_scoreprint('R2 score for age: ', r2_score(age_true, age_pred))

R2 年龄得分:0.582979466456328

参考

http://aicip.eecs.utk.edu/wiki/UTKFace UTK 人脸数据集:

Keras 多输出文档:https://keras.io/getting-started/functional-api-guide/

SanjayaSubedi 关于多输出模型的帖子:https://sanjayasubdi . com . NP/deep learning/multi output-keras/

时尚网上的 PyImageSearch 帖子:https://www . PyImageSearch . com/2018/06/04/keras-multiple-outputs-and-multiple-loss/

剧情:https://plot.ly/

构建朴素贝叶斯分类器:预测酒店取消

原文:https://towardsdatascience.com/building-a-naive-bayes-classifier-predicting-hotel-cancellations-31e3b8766614?source=collection_archive---------36-----------------------

在此示例中,构建了一个朴素贝叶斯分类器,以预测可能取消酒店预订的客户。

朴素贝叶斯分类器是一种概率分类器,也是最基本的分类模型之一。我们称该分类器为“幼稚”分类器的原因是因为该分类器天真地假设数据集中的所有特征相互独立,即条件独立。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:照片由埃里克·斯坦皮克斯拜拍摄。

从这个角度来看,特征选择通常不是朴素贝叶斯分类器的优势。假设所有的特征都是相互独立的,那么当特征之间存在显著的相关性时,这个分类器就有表现不佳的风险。

贝叶斯定理定义如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:作者图片

这个定理的基础是条件概率,即给定前一个结果的发生,一个结果发生的概率。在这种情况下,特定事件发生的概率随着新信息的收集而更新。

对于这个特定的场景,我们希望找到答案——在给定先验概率的情况下,客户取消酒店预订的概率是多少?

在这种情况下,将应用高斯朴素贝叶斯分类算法。尽管结果变量是分类变量(1 =取消,0 =不取消),但模型中包含的许多特征是连续的。因此,假设这些连续特征按照正态(高斯)分布分布。

数据操作

在这点上,在运行朴素贝叶斯模型之前不会进行特征选择。该模型使用以下功能构建:

  1. 交付周期
  2. 到达日期年份
  3. arrivaldateweekno
  4. 抵达日期
  5. staysweekendnights
  6. 周末夜
  7. 成年人
  8. 婴儿
  9. isrepeatedguest
  10. 以前的取消
  11. 先前预订未取消
  12. 预订变更
  13. dayswaitinglist
  14. adr
  15. RCP
  16. 总计数量
  17. 到达日期月
  18. 国家
  19. 细分市场
  20. 分销渠道
  21. reservedroomtype
  22. assignedroomtype
  23. 沉积类型
  24. 客户类型

定义了区间(或连续随机变量)。举两个例子:

leadtime = train_df['LeadTime']
adr = train_df['ADR']

使用’'‘类别代码’ ’ '定义具有分类成分的变量。

作为另外两个例子:

deposittypecat=train_df.DepositType.astype("category").cat.codes
deposittypecat=pd.Series(deposittypecat)
customertypecat=train_df.CustomerType.astype("category").cat.codes
customertypecat=pd.Series(customertypecat)

numpy 列堆栈是为独立变量(连续变量和分类变量)制定的:

x1 = np.column_stack((leadtime,arrivaldateyear,arrivaldateweekno,arrivaldatedayofmonth,staysweekendnights,staysweeknights,adults,babies,isrepeatedguestcat,previouscancellations,previousbookingsnotcanceled,bookingchanges,dayswaitinglist,adr,rcps,totalsqr,arrivaldatemonthcat,mealcat,countrycat,marketsegmentcat,distributionchannelcat,reservedroomtypecat,assignedroomtypecat,deposittypecat,customertypecat))
x1 = sm.add_constant(x1, prepend=True)

然后,数据被分成训练集和验证集:

X_train, X_val, y_train, y_val = train_test_split(x1, y1)

精确度与召回率和 f1 分数

在我们运行模型之前,让我们稍微谈一下精度召回

当比较准确度分数时,我们看到在每个混淆矩阵中都提供了大量的读数。

Precision = ((True Positive)/(True Positive + False Positive))Recall = ((True Positive)/(True Positive + False Negative))

这两个读数经常相互矛盾,也就是说,通常不可能在不降低召回率的情况下提高精确度,反之亦然。

对理想指标的评估很大程度上取决于所分析的具体数据。例如,癌症检测筛查出现假阴性(即表明患者没有患癌症,而事实上他们患有癌症)是一大禁忌。在这种情况下,召回是理想的衡量标准。

然而,对于电子邮件,人们可能更喜欢避免误报,例如,将一封重要的电子邮件发送到垃圾邮件文件夹,而实际上它是合法的。

f1 分数在设计一个更通用的分数时考虑了精确度和召回率。

哪个因素对预测酒店取消更重要?

从酒店的角度来看,他们可能希望更准确地识别出最终会取消预订的客户,这使得酒店能够更好地分配房间和资源。确定不打算取消预订的客户不一定会增加酒店分析的价值,因为酒店知道,无论如何,很大一部分客户最终都会坚持预订。

模型配置和结果

上面概述的相关特征包括在内,用于确定客户是否将取消他们的预订。

GaussianNB 库是从 scikit-learn 导入的:

from sklearn.naive_bayes import GaussianNB

高斯朴素贝叶斯模型被定义为:

>>> gnb = GaussianNB()
>>> gnbGaussianNB(priors=None, var_smoothing=1e-09)

在验证集上生成预测:

>>> y_pred = gnb.fit(x1_train, y1_train).predict(x1_val)
>>> y_predarray([1, 1, 0, ..., 0, 1, 1])

将预测与来自验证集的实际结果进行比较,生成混淆矩阵:

>>> from sklearn.metrics import classification_report,confusion_matrix
>>> print(confusion_matrix(y1_val,y_pred))
>>> print(classification_report(y1_val,y_pred))[[2842 4424]
 [ 165 2584]]
              precision    recall  f1-score   support 0       0.95      0.39      0.55      7266
           1       0.37      0.94      0.53      2749 accuracy                           0.54     10015
   macro avg       0.66      0.67      0.54     10015
weighted avg       0.79      0.54      0.55     10015

第 1 类的召回率为 94% ,而 f1 分数的准确率为 54% 。现在,让我们在 H2(测试集)上测试预测性能。

[[ 7863 38365]
 [ 2722 30380]]
              precision    recall  f1-score   support 0       0.74      0.17      0.28     46228
           1       0.44      0.92      0.60     33102 accuracy                           0.48     79330
   macro avg       0.59      0.54      0.44     79330
weighted avg       0.62      0.48      0.41     79330

我们看到,1 类的召回率略微下降至 92% ,而 f1 分数的准确率为 48%

显然,在较高的召回率和总体较高的准确率之间存在一种权衡。假设数据集中的大多数条目为 0(未取消),那么有理由认为具有总体高准确率的模型在预测未取消方面表现得相当好,但是在预测 1 条目(取消)方面表现不佳。

例如,当对此数据集运行 SVM 时,会获得以下结果:

[[25217 21011]
 [ 8436 24666]]
              precision    recall  f1-score   support 0       0.75      0.55      0.63     46228
           1       0.54      0.75      0.63     33102 accuracy                           0.63     79330
   macro avg       0.64      0.65      0.63     79330
weighted avg       0.66      0.63      0.63     79330

我们看到 f1 分数的准确率高达 63%,但是 1 类的召回率下降到 75%。

在这方面,如果希望优先识别取消,而不是最大化整体准确性,那么可以认为朴素贝叶斯模型在这种情况下工作得更好。然而,应该记住,最大化回忆只在一定程度上起作用。如果召回率为 100%,那么所有预订都可以归类为取消,这并不能揭示取消预订的客户和未取消预订的客户之间的差异。

限制

如前所述,朴素贝叶斯认为所有特征都是独立的。在这方面,在变量之间存在强烈的条件依赖性的情况下,该模型有表现不佳的风险,例如,假设在来自特定来源国的客户中观察到更多的取消。但是,我们也观察到,在特定的细分市场中,这些取消率要高得多。

在这方面,考虑到这两个特征之间的条件依赖性,我们不能再确定来自特定来源国的客户的取消率更高,因为来自特定细分市场的更高数量的客户可能正好出现在来自该特定国家的预订中。

特征选择是机器学习的重要组成部分,事实上,有许多模型能够解释哪些特征对影响结果变量的贡献更大,如下例所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来源:InterpretML 输出

但是,朴素贝叶斯假设所有要素的权重基本相等,这可能会根据数据显著影响读数的准确性。

结论

在这个例子中,我们看到了如何在 Python 中构建朴素贝叶斯模型,以及如何使用精度和召回率来评估模型的准确性。

本例的数据集和笔记本可从 MGCodesandStats GitHub 库获得,以及对该主题的进一步研究。

免责声明:本文是在“原样”的基础上编写的,没有担保。本文旨在提供数据科学概念的概述,不应以任何方式解释为专业建议。

参考

从 Twitter 数据中构建网络图

原文:https://towardsdatascience.com/building-a-network-graph-from-twitter-data-a5e7b8672e3?source=collection_archive---------15-----------------------

编写 Java 应用程序来收集 Twitter 数据,并以图表的形式显示出来。

在本文中,我们将构建一个数据科学项目。我们从 Twitter 上收集数据,因为它有大量的数据,这让我们可以获得这些数据。我们更喜欢 Java,因为它是一种编译语言,并且有强大的并发库。最后,我们使用开源图形平台 Gephi 对这些数据进行总结。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 Gephi 从 9/14/20 的样本推文中生成的图表。语言是 TH

我们需要以下做这个项目:

  • Java IDE。我们的选择是日蚀。
  • Twitter4j 库。从这里获取 jar 文件和教程。
  • Twitter 开发者账号。我们需要这个来调用 Twitter API。有一些资源提到了如何获得访问权限。
  • 任何符合 JDBC 标准的数据库。我们使用 Sqlite。它很轻。不需要安装软件。没有守护进程。只需将 Sqlite jar 文件复制到项目中。但是,有一些限制需要解决。
  • Gephi 是一个开源的图形工具。从这里下载。

顺便说一下,读者可以使用他们喜欢的任何语言或平台,Python 或 Node.js。

以下是构建 Twitter 网络图的步骤:

  • 收集推文和用户,并保存到数据库中。
  • 检索用户的朋友。从上一步的用户列表中,获取这些用户的朋友。我们将把这些保存到表格中
  • 筛选我们希望在图表中看到的数据
  • 将数据导出到 CSV 文件
  • 将 CSV 文件导入 Gephi。做一些格式化,布局。我们会得到一个 twitter 社交图

收集推文和用户

第一步,我们收集样本 tweets,然后将它们写到表中。为此:

  • 创建一个 twitter 流对象。对溪流进行采样。API 提供了所有 tweets 的随机子集。
  • 对于收到的每条 tweet,向 executor 服务提交一个可调用的任务。该任务将执行数据库操作和/或进一步的处理。

下面是代码:

可调用任务中的代码会将 tweets 和相关对象(如用户)保存到表中。通过使用 executor 服务,我们分离了 tweet 处理和数据库相关的任务。即使有时推文的速度超过了数据库的处理速度,我们的应用程序仍然不会错过任何东西。此外,因为我们使用 Sqlite 数据库,并且在一个时刻只能有一次对数据库的写入,所以 executor 服务必须是单线程 Executor。以下是该任务的部分代码:

检索用户的朋友

从上一步,我们得到了一个用户列表,我们想知道他们所有的朋友。Twitter API 返回指定用户的朋友 id,但单次请求不会超过 5000 个 id。如果该用户拥有更多,我们需要多次呼叫。此外,Twitter 有费率限制。它只允许每个 15 分钟的窗口有 15 个请求。基本上每分钟 1 个请求。

所以,我们得到了朋友 id。需要其他 API 调用来将用户 id 转换为用户对象。Twitter 为此提供了 API。对于每个请求,我们可以查询多达 100 个用户 id。对此速率限制是每 15 分钟窗口 300 个请求。所以,是每分钟 20 个请求。

Twitter API 和速率限制的更多细节点击这里

为了有效地处理速率限制,我们将有两个线程。第一个线程将调用朋友 id 查询。第二个线程将执行用户查找部分。朋友查找线程将通过阻塞队列将用户 id 传递给用户查找线程。基本上,我们在这里使用生产者-消费者模式。

朋友查找线程

以下代码是 FriendsLookupRunnable 的一部分。

一些要点:

  • 这个 runnable 的 run 方法将从要处理的用户 id 阻塞队列中轮询一个用户 id。
  • 对于每个 id,调用 getFriendIds 方法。这个方法返回朋友 id 列表。每个用户 id 和朋友 id 对都被插入到 User_friend 表中。
  • 得到朋友 id 也被放入另一个阻塞队列。这些 id 将由另一个线程检索并进行处理。
  • getFriendIds 方法跟踪最后一次调用它的时间,并通过使用 Thread.sleep()确保每次调用之间有足够的延迟(1 分钟)。
  • 即使我们这样做了,也很少有超出速率限制的异常情况发生。因此,我们捕获 TwitterException 并比较异常状态代码。如果超过了速率限制,我们就重试查询。
  • 还有一些其他的例外。例如,当用户受到保护时,twitter API 会给你一个未经授权的错误。

以下是创建 User_Friend 表的命令,该表存储第一个线程的结果:

CREATE TABLE User_Friend ( user_id       INT (8), friend_id     INT (8), PRIMARY KEY (user_id,friend_id));

用户查找线程

下面的代码是 UsersLookupRunnable 类。

以下是一些要点:

  • 在 run 方法中,有一个 while 循环从队列中检索用户 id。然后它将调用 lookupUsers 方法进行实际的查找
  • 因为 Twitter lookupUsers API 一次只能处理不超过 100 个用户 id,所以在调用 Twitter API 之前,我们将把一个用户 id 数组分割成包含 100 个或更少元素的数组。
  • lookupUsers 方法跟踪上次调用它的时间,并通过使用 Thread.sleep()确保每次调用之间有足够的延迟(3 秒)。
  • 该方法返回将被插入到用户表中的用户列表。表格的结构应该类似于 Twitter 用户界面。

以下是创建存储第二个线程结果的用户表的命令:

CREATE TABLE User ( id              INT (8)       PRIMARY KEY, name            VARCHAR (100), screen_name     VARCHAR (100), description     VARCHAR (255), email           VARCHAR (50), favorites_count INT, followers_count INT, friends_count   INT, statuses_count  INT, lang            VARCHAR (10), location        VARCHAR (255), url             VARCHAR (255), imageurl        VARCHAR (255), is_protected    INT (1), is_verified     INT (1), created         VARCHAR (20), last_modified   VARCHAR (20));

主要的方法是:

  • 设置数据库连接
  • 创建 2 个阻塞队列
  • 准备用户 id 列表。将其添加到第一个阻塞队列中。
  • 创建 2 个可运行线程和 2 个线程。
  • 启动两个线程。
  • 添加关机挂钩。所以,当进程终止时,它会中断两个线程。
  • 等到两个线程都完成。
  • 清理数据库

代码应该如下所示:

过滤数据(可选)

有时,我们希望只看到全部数据的一部分。这样做很简单,因为数据已经在 SQL 表中了。假设我们想看看在我们的样本推文中拥有最多关注者的前 100 名用户是如何相互关注的。以下是要做的事情:

  • 创建用于存储结果的表格。下面是使用的 SQL 语句:
CREATE TABLE Graph_Friend_Edge ( Source      INT, Target      INT );
CREATE TABLE Graph_Friend_Node ( id              INT      PRIMARY KEY, label           VARCHAR (50), name            VARCHAR (100),);
  • 仅用顶级用户填充边缘表。以下是 SQL 语句:
insert into graph_friend_edge(source, target)select user_id, friend_id from user_friendjoin user u1 on friend_id=u1.idjoin user u2 on user_id=u2.idwhere user_id in(select friend_id from user_friendgroup by friend_id order by count(*) desc limit 100)and friend_id in(select friend_id from user_friend group by friend_id order by count(*) desc limit 100);
  • 然后,用以下 SQL 填充节点表:
insert into graph_friend_node(id, label, name)select n.id, u.screen_name, u.namefrom(select source id from graph_friend_edgeunionselect target id from graph_friend_edge) n join user u on n.id = u.id;

将数据导出到 CSV 文件

这部分很简单。使用数据库工具将数据导出到 CSV 文件。

  • 将 user_friend 表导出到 edge CSV 文件。
  • 将用户表导出到节点 CSV 文件。

创建网络图

Gephi 是一个开源的图形分析和可视化工具。外面有很多 Gephi 教程。看一看这里。导入 CSV 文件教程,在这里找到

以下是在我们的项目中要做的步骤概述:

  • 打开 Gephi。创建新项目。
  • 导入边和节点 CSV 文件。最初的图表可能看起来不像这样:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

9/14/20 应用样本推文布局前的前 100 名用户好友图表。语言是 TH

我们需要显示节点标签。配置节点大小和颜色。应用一些布局。

  • 启用节点标签
  • 配置与入度(传入边的数量)成比例的节点大小和标签大小
  • 选择“ForceAtlas2”的布局并运行它。
  • 运行社区检测算法
  • 根据模块等级设置节点颜色。这将根据它检测到的社区给节点着色。

完成这些后,图表看起来更有意义:

  • 用户的屏幕名称显示为节点标签。
  • 该组中拥有更多追随者的节点看起来更大。
  • 边,箭头线,代表跟随关系
  • 根据图算法,具有相同颜色的节点在相同的社区中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用 Gephi 从 9/14/20 的样本推文中生成的前 100 名用户的朋友图。语言是 TH

结论

我们构建了 Java 应用程序来从 Twitter 收集推文、用户和朋友数据,并将其放入关系数据库。我们对数据做了一些过滤。然后,导入到 Gephi,图形平台和可视化工具,产生一个社会网络图。

这只是我们利用 Twitter 数据所能做的很小一部分。Gephi 能给我们提供更多的东西。此外,还有更多图形分析平台。例如,Neo4j 可以让我们将数据存储到它的数据库中,并运行图形算法。

用 PyTorch Lightning 在亚马逊 SageMaker 上构建神经网络

原文:https://towardsdatascience.com/building-a-neural-network-on-amazon-sagemaker-with-pytorch-lightning-63730ec740ea?source=collection_archive---------25-----------------------

用现代工具为每个数据科学家民主化人工智能

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片由com break来自 Pixabay 。闪电的力量可以点燃火炬。

在现实世界的应用中,托管人工智能服务,如亚马逊认知亚马逊理解为专门的数据科学团队从零开始构建模型提供了一个可行的替代方案。即使当用例需要使用定制图像标签或文本实体等专门构建的数据集进行模型重新训练时,也可以使用 Amazon Rekognition 定制标签Amazon understand 定制实体轻松实现。

这些服务提供最先进的机器学习模型实现,涵盖几个用例。这种模型在某些情况下并不可行。这可能是因为底层网络需要根据数据进行深度定制,科学家需要实施先进的网络架构,如 LSTMs、GANs、OneShot learners、强化学习模型甚至模型集成。

在机器学习中,研究和模型构建是一项永无止境的工作,每天都开放一套全新的功能。然而,通常需要一个由不同专业人员组成的大型团队来构建从神经网络架构定义到生产部署的模型。

Amazon SageMaker 旨在为每个人实现机器学习的民主化,提供了一套针对数据科学家和软件工程师的工具。截至 2020 年,亚马逊 SageMaker (SM)是一套工具,专门用于数据集标签化(SM GroundTruth)、模型开发(SM 笔记本)、分布式训练和推理部署(SM 模型/端点)以及实验创建、调试和监控(SageMaker Studio)。

短短几年间,出现了许多深度学习框架,从 TensorFlow、Apache MXNet 和 PyTorch 开始,每一个都提高了模型创建和定制的门槛。最有前途的技术之一,由于它在动态计算图形定义和数据并行性支持方面的灵活性。

有了闪电,PyTorch 变得既简单又强大。

Amazon SageMaker 从第一天起就引入了对 PyTorch 的支持,并在过去几年中建立了稳定的用户基础。然而,PyTorch 错过了 Keras(tensor flow)等替代方案的简单性、低学习曲线和高抽象级别。开发了一些框架来填补空白,例如优秀的 Fast.ai 库,旨在为接近 PyTorch 的开发人员提供一个简单易学的解决方案。

2019 年,为了让机器学习的努力成为一个共同点,威廉·猎鹰发布了第一个生产就绪版本的 PyTorch Lightning ,这是一个架构 PyTorch 项目的框架,获得了对更少样板文件和改进代码阅读的支持。

在本文中,我们将从一个简单的神经网络创建开始,遵循一个统一的工作流程,在 Amazon SageMaker 上开发、测试和部署一个机器学习模型,并有一个循序渐进的教程,侧重于初学者。不需要预先了解 Amazon SageMaker 或 PyTorch,即使它有助于理解一些语言 API。

MNIST 是新的“你好世界”

我们将从一个用于手写数字识别的简单神经网络开始,使用著名的 MNIST 数据集。用例非常狭窄,但近年来,由于结果模型的简单性,它已经成为使用神经网络进行图像处理的“Hello World”。

亚马逊 SageMaker 笔记本

处理机器学习项目的第一步是在一些实验环境中建立模型。亚马逊 SageMaker 笔记本提供 JupyterLab 环境的简单设置。PyTorch 通过 torchvision 库提供了一个准备好的数据集。由于本文想要展示一个适用于通用模型训练的工作流,我们决定不使用 PyTorch 数据集,而是从互联网上下载 MNIST 图像并保存到 S3 桶中。

当使用 SageMaker Studio 构建模型时,我们建议在本地下载一堆数据,以加快开发和测试速度。我们可以使用以下命令轻松做到这一点:

现在,我们可以显示一些随机数据,只是为了在开始构建 Lightning 模型之前更好地理解它是如何组织的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

手写数字图像的小 MNIST 数据集。

MNIST 分类器和亚马逊 SageMaker

在我们建立 PyTorch 评估之后,Amazon SageMaker 管理来自 Python 代码的代码运行。估计器是一个类,包含训练所需的所有必需参数(或在 SageMaker 容器上运行的推理脚本)。

上图显示了如何为 PyTorch 创建 SageMaker 估计器。详细解释代码中的注释。

为了执行具有卷积层的神经网络的训练,我们必须使用 GPU 在 ml.p2.xlarge 实例上运行我们的训练作业。

Amazon Sagemaker 默认将训练代码放在我们项目中的代码文件夹中,但是它的路径可以在实例化 Estimator 时被覆盖。训练脚本是 PyTorch 闪电的神奇之处。

我们模特的培训班。详细解释代码中的注释。

我们的训练器可以在本地 GPU 平台或 Amazon SageMaker 容器上运行,无需任何更改。

Amazon SageMaker 的神奇之处在于默认为教练和模型参数的环境变量。在一个容器中,这些变量被设置到文件夹中,这些文件夹是在运行我们的脚本之前从 S3 复制的,在训练完成之后又被复制回 S3。

在这一点上,我们还没有定义一个模型,只是映射了一些变量并配置了一个估计器对象,但是一些 Lightning 特定的构造已经可见,例如训练器类。

Trainer,顾名思义,是一个 Python 类,能够抽象所有训练工作流步骤,加上一系列日常操作,如在每个纪元后保存模型检查点。培训师自动执行一系列活动,例如寻找最佳学习率确保可重复性、设置并行培训的 GPU 和多节点后端的数量、更多

Lightning 提供了一组默认设置,让训练变得超级简单。值可以被覆盖,因为它完全控制整个生命周期,因为我们的分类器类必须符合协议。

让我们分解代码,检查每一步发生了什么

1。导入库并扩展 LightningModule

每个 PyTorch Lightning 实现都必须扩展 base pl。继承自 nn 的 LightningModule 类。模块增加了一些实用方法。

2。准备网络层

在类构造函数中,我们准备了网络层,用于以后构建计算图。卷积层从图像中提取特征,并传递给随后的层,增加了非线性和随机性。

3.为训练、验证和测试数据集构建数据加载器

数据加载器类是从 PyTorch 图像加载器精心制作的。混洗和分割确保了从训练图像构建的随机验证数据集。

4.实施培训师要求的实用方法

PyTorch Lightning 实施了一个标准的项目结构,要求分类器实现某些方法,这些方法将由训练器类在执行训练和验证时调用。

5.机具向前传递

forward 方法相当于传统的 PyTorch forward 函数,必须实现该函数才能构建计算图。

6.实施培训步骤

训练器对每个图像批次调用训练步骤方法,计算网络预测和它们的相对损失函数。

7.验证计算和堆叠

Lightning 提供了对一组可选方法的支持,如 validation_step 、 *validation_epoch_end、*和 validation_end ,以允许开发人员定义如何计算验证损失并堆叠结果,从而在训练期间找到改进。这些方法要求代码返回符合特定模式的数据,然后 PL 以 TensorBoard 兼容格式输出所有指标。

可以实现等效的方法来支持模型测试,这在投入生产之前是非常鼓励的。

现在我们准备好让我们的模型旋转起来,并开始用 Amazon SageMaker 进行训练。

亚马逊 SageMaker 上的模型培训

训练开始从命令行或另一个 Jupyter 笔记本运行 main.py 。它也可以从 AWS Lambda 函数运行,由 AWS Step 函数调用,以使训练过程完全可脚本化且无需服务器。然而,日志被收集到控制台中,并被推送到 Amazon CloudWatch 以供进一步检查。当启动多个训练任务来微调超参数时,此功能非常有用。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

亚马逊 SageMaker 培训作业的控制台输出。

Amazon SageMaker 代表我们启动 p2.xlarge 实例,然后将输入数据下载到容器中并启动我们的代码,在安装了我们的 requirements.txt 文件中的所有依赖项之后,启动 main.py、

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

亚马逊 SageMaker 培训作业的控制台输出。

Amazon SageMaker 以 JSON 格式构建一个作业描述符,并将其传递给训练上下文。在这个对象中,所有参数被发送到训练作业,输入目录被映射到 /opt/ml/ 子文件夹,从 S3 接收数据,输出被收集到结果桶中。训练代码也打包在不同的 S3 路径中,然后下载到容器中。

最后,在启动我们的训练脚本之前,环境变量被设置为标准的 SageMaker 值。

几分钟后,由于我们只训练了六个纪元,我们的验证显示出来,保存的模型被上传到 S3。由于 PyTorch Lightning 代表我们自动保存模型检查点,并且我们将其输出目录映射到 output_data_dir,因此我们还可以从 S3 收集中间检查点和验证数据,以备 TensorBoard 处理和分析。

S3 上有一个分类模型,可用于推理脚本、Amazon SageMaker 端点,或者使用 JIT 编译器部署在边缘设备上。

从这里去哪里?

在这篇文章中,我们讨论了 Amazon SageMaker 和 PyTorch Lightning 如何合作实现深度学习的民主化,减少了每个开发人员或数据科学家从零开始构建模型到生产的样板文件。Amazon SageMaker 只用几行代码就减轻了启动和配置训练机器的负担。同时,Lightning 使梯度管理、优化和反向传播等步骤变得透明,使研究人员能够专注于神经网络架构。

该项目的完整代码可在 GitHub 上获得。它可以作为一个独立的脚本在任何 PC 上运行,只需启动

安装 pipenv 的简单 shell 命令,然后运行我们的脚本。感谢 Amazon SageMaker,我们不需要任何 GPU 来训练我们的模型

如果你喜欢 Jupyter 笔记本界面,同样的代码可以在亚马逊 SageMaker、内运行,只需运行Notebook/SageMaker _ deploy . ipynb。由于 SageMaker 启动培训作业,因此不需要有 GPU 实例来运行笔记本。

本文只是一个展示 SageMaker 和 Lightning 如何协同工作的示例项目。尽管如此,它仍可用作图像分类等计算机视觉任务的起点,只需将网络架构更改为类似 VGG 或 ResNet,并提供足够的数据集。

在接下来的文章中,我们将深入研究图像处理的机器学习生产管道,并介绍我们在 Neosperience 中采用的一些架构解决方案,以实现图像记忆和客户行为分析。敬请期待!

我的名字是卢卡·比安奇。我是**在T5**的首席技术官 , 无服务器设计模式和最佳实践 的作者。近十年来,我一直在 AWS 上为大规模生产工作负载构建软件架构。

Neosperience Cloud 是面向品牌的一站式 SaaS 解决方案,旨在带来技术上的共鸣,利用机器学习方面的创新为 1:1 客户体验提供支持。

你可以通过 TwitterLinkedIn 联系我。

使用 Numpy 构建单隐层神经网络

原文:https://towardsdatascience.com/building-a-neural-network-with-a-single-hidden-layer-using-numpy-923be1180dbf?source=collection_archive---------9-----------------------

使用 Numpy 实现具有单个隐藏层的两类分类神经网络

上一篇中,我们讨论了如何使用 NumPy 制作一个简单的神经网络。在本帖中,我们将讨论如何制作一个具有隐藏层的深度神经网络。

  1. 导入库

我们将导入一些基本的 python 库,如 numpy、matplotlib(用于绘制图形)、sklearn(用于数据挖掘和分析工具)等。这是我们需要的。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split

2。数据集

我们将使用钞票数据集,该数据集涉及在给定从照片中获取的几个测量值的情况下预测给定钞票是否是真实的。这是一个二元(2 类)分类问题。有 1,372 个具有 4 个输入变量和 1 个输出变量的观察值。更多详情请参见链接。

data = np.genfromtxt(‘data_banknote_authentication.txt’, delimiter = ‘,’)
X = data[:,:4]
y = data[:, 4]

我们可以使用散点图来可视化数据集。我们可以看到两类(真实和非真实)是可分的。我们的目标是建立一个模型来拟合这些数据,也就是说,我们希望建立一个神经网络模型来定义区域是真实的还是不真实的。

plt.scatter(X[:, 0], X[:, 1], alpha=0.2,
 c=y, cmap=’viridis’)
plt.xlabel(‘variance of wavelet’)
plt.ylabel(‘skewness of wavelet’);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在,让我们将数据分为训练集和测试集。这可以使用 sk learn*train _ test _ split()*函数来完成。选择 20%的数据用于测试,80%用于训练。此外,我们将检查训练集和测试集的大小。这将有助于以后设计我们的神经网络模型。

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)X_train = X_train.T
y_train = y_train.reshape(1, y_train.shape[0])X_test = X_test.T
y_test = y_test.reshape(1, y_test.shape[0])print (‘Train X Shape: ‘, X_train.shape)
print (‘Train Y Shape: ‘, y_train.shape)
print (‘I have m = %d training examples!’ % (X_train.shape[1]))

print ('\nTest X Shape: ', X_test.shape)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3。神经网络模型

构建神经网络的一般方法是:

1\. Define the neural network structure ( # of input units,  # of hidden units, etc). 
2\. Initialize the model's parameters
3\. Loop:
    - Implement forward propagation
    - Compute loss
    - Implement backward propagation to get the gradients
    - Update parameters (gradient descent)

我们将构建一个具有单一隐藏层的神经网络,如下图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.1 定义结构

我们需要定义输入单元的数量、隐藏单元的数量和输出层。输入单位等于数据集中的要素数量(4),隐藏层设置为 4(为此),问题是我们将使用单一图层输出的二进制分类。

def **define_structure**(X, Y):
    input_unit = X.shape[0] # size of input layer
    hidden_unit = 4 #hidden layer of size 4
    output_unit = Y.shape[0] # size of output layer
    return (input_unit, hidden_unit, output_unit)(input_unit, hidden_unit, output_unit) = **define_structure**(X_train, y_train)
print("The size of the input layer is:  = " + str(input_unit))
print("The size of the hidden layer is:  = " + str(hidden_unit))
print("The size of the output layer is:  = " + str(output_unit))

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.2 初始化模型参数

我们需要初始化权重矩阵和偏置向量。当偏差设置为零时,权重被随机初始化。这可以使用下面的函数来完成。

def **parameters_initialization**(input_unit, hidden_unit, output_unit):
    np.random.seed(2) 
    W1 = np.random.randn(hidden_unit, input_unit)*0.01
    b1 = np.zeros((hidden_unit, 1))
    W2 = np.random.randn(output_unit, hidden_unit)*0.01
    b2 = np.zeros((output_unit, 1))
    parameters = {"W1": W1,
                  "b1": b1,
                  "W2": W2,
                  "b2": b2}

    return parameters

3.3.1 正向传播

对于正向传播,给定一组输入特征(X),我们需要计算每一层的激活函数。对于隐藏层,我们使用 tanh 激活函数:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

同样,对于输出层,我们使用 sigmoid 激活函数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以使用下面的代码来实现向前传播。

def **sigmoid**(z):
    return 1/(1+np.exp(-z))def **forward_propagation**(X, parameters):
    W1 = parameters['W1']
    b1 = parameters['b1']
    W2 = parameters['W2']
    b2 = parameters['b2']

    Z1 = np.dot(W1, X) + b1
    A1 = np.tanh(Z1)
    Z2 = np.dot(W2, A1) + b2
    A2 = sigmoid(Z2)
    cache = {"Z1": Z1,"A1": A1,"Z2": Z2,"A2": A2}

    return A2, cache

3.3.2 计算成本

我们将计算交叉熵成本。在上一节中,我们计算了 A2。使用 A2,我们可以使用以下公式计算交叉熵成本。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

def **cross_entropy_cost**(A2, Y, parameters):
    # number of training example
    m = Y.shape[1] 
    # Compute the cross-entropy cost
    logprobs = np.multiply(np.log(A2), Y) + np.multiply((1-Y), np.log(1 - A2))
    cost = - np.sum(logprobs) / m
    cost = float(np.squeeze(cost))

    return cost

3.3.3 反向传播

我们需要计算不同参数的梯度,如下所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图片提供:吴恩达

def **backward_propagation**(parameters, cache, X, Y):
    #number of training example
    m = X.shape[1]

    W1 = parameters['W1']
    W2 = parameters['W2']
    A1 = cache['A1']
    A2 = cache['A2']

    dZ2 = A2-Y
    dW2 = (1/m) * np.dot(dZ2, A1.T)
    db2 = (1/m) * np.sum(dZ2, axis=1, keepdims=True)
    dZ1 = np.multiply(np.dot(W2.T, dZ2), 1 - np.power(A1, 2))
    dW1 = (1/m) * np.dot(dZ1, X.T) 
    db1 = (1/m)*np.sum(dZ1, axis=1, keepdims=True)

    grads = {"dW1": dW1, "db1": db1, "dW2": dW2,"db2": db2}

    return grads

3.3.4 梯度下降(更新参数)

我们需要使用梯度下降规则更新参数,即

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其中 𝛼 是学习率 𝜃 是参数。

def **gradient_descent**(parameters, grads, learning_rate = 0.01):
    W1 = parameters['W1']
    b1 = parameters['b1']
    W2 = parameters['W2']
    b2 = parameters['b2']

    dW1 = grads['dW1']
    db1 = grads['db1']
    dW2 = grads['dW2']
    db2 = grads['db2'] W1 = W1 - learning_rate * dW1
    b1 = b1 - learning_rate * db1
    W2 = W2 - learning_rate * dW2
    b2 = b2 - learning_rate * db2

    parameters = {"W1": W1, "b1": b1,"W2": W2,"b2": b2}

    return parameters

4。神经网络模型

最后,把所有的功能放在一起,我们可以建立一个只有一个隐藏层的神经网络模型。

def **neural_network_model**(X, Y, hidden_unit, num_iterations = 1000):
    np.random.seed(3)
    input_unit = **define_structure**(X, Y)[0]
    output_unit = **define_structure**(X, Y)[2]

    parameters = **parameters_initialization**(input_unit, hidden_unit, output_unit)

    W1 = parameters['W1']
    b1 = parameters['b1']
    W2 = parameters['W2']
    b2 = parameters['b2']

    for i in range(0, num_iterations):
        A2, cache = **forward_propagation**(X, parameters)
        cost = **cross_entropy_cost**(A2, Y, parameters)
        grads = **backward_propagation**(parameters, cache, X, Y)
        parameters = **gradient_descent**(parameters, grads)
        if i % 5 == 0:
            print ("Cost after iteration %i: %f" %(i, cost)) return parametersparameters = **neural_network_model**(X_train, y_train, 4, num_iterations=1000)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

5。预测

使用学习到的参数,我们可以通过使用前向传播来预测每个示例的类。

def **prediction**(parameters, X):
    A2, cache = forward_propagation(X, parameters)
    predictions = np.round(A2)

    return predictions

如果*激活> 0.5,*则预测为 1 否则为 0。

predictions = **prediction**(parameters, X_train)
print ('Accuracy Train: %d' % float((np.dot(y_train, predictions.T) + np.dot(1 - y_train, 1 - predictions.T))/float(y_train.size)*100) + '%')predictions = **prediction**(parameters, X_test)
print ('Accuracy Test: %d' % float((np.dot(y_test, predictions.T) + np.dot(1 - y_test, 1 - predictions.T))/float(y_test.size)*100) + '%')

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

正如我们所看到的,训练精度约为 97%,这意味着我们的模型正在工作,并且以高概率拟合训练数据。测试准确率在 96%左右。给定简单的模型和小的数据集,我们可以认为它是一个好的模型。

在这里成为 Medium 会员,支持独立写作,每月 5 美元,可以完全访问 Medium 上的每个故事。

从头开始构建新闻聚合器:新闻过滤、分类、线索分组和排名

原文:https://towardsdatascience.com/building-a-news-aggregator-from-scratch-news-filtering-classification-grouping-in-threads-and-7b0bbf619b68?source=collection_archive---------9-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

假新闻头条来自https://www . design boom . com/design/the-fake-news stand-TBWA-chiat-day-Columbia-journalism-review-11-05-2018/

这篇文章背后的想法是展示一种合理简单的方法,人们可以在几周内实现这种方法来解决现实世界中的一个问题,即创建一个新闻聚合器,如谷歌新闻Yandex 新闻,从网上搜集的数百万条新闻中显示最热门的新闻线索。

问题陈述和限制

所以这是另一篇关于 NLP 的文章,我将描述在电报数据聚类竞赛中开发的文本过滤、分类、分组和排序的一些算法。这篇文章背后的动机是为了证明你可以构建一个像样的文本处理系统,甚至不需要 GPU 就可以在你的笔记本电脑上运行。

比赛包括五项任务——检测新闻语言,从其他文本中过滤新闻(如百科文章、一些随机的娱乐帖子、博客帖子等),从七个类别(社会、经济、体育、科学、技术、娱乐和其他)中选择一个进行新闻分类,将新闻按线索分组,并根据重要性对这些线索进行排序。此处有完整的比赛规则

特定算法和工具的选择在很大程度上取决于竞赛规则,这些规则对实施施加了一些限制——每个任务必须在 60 秒内执行,每 1000 篇文章一台 Debian 机器,有 8 个 CPU 和 16 Gb 的 RAM,不应该使用外部服务或 API,算法甚至不应该假设有互联网连接(例如,下载一些预训练的模型)。因此,没有像伯特,阿尔伯特或 GPT-2 SOTA 变压器模型应该被涉及。

本文中描述的高级解决方案体系结构如下所示:

  1. 文本预处理和矢量化
  2. 用具有 LSTM 和注意层的定制深度神经网络(DNN)进行文本分类
  3. 利用由每个组内的 Levenshtein 距离控制的最近邻搜索算法对线索中的文本进行分组。
  4. 按重要性排列新闻线索

注意:这篇文章中描述的所有代码和想法都是在比赛期间(2 周)开发的,尽管分组算法和代码样式的一些微调是在新年假期之后执行的。

原始数据解析

竞赛参与者被提供成千上万的出版物,这些出版物被保存为 html 文件,包含新闻标题、文本,有时还包含图像、出版日期、作者和媒体来源。我使用 Beatifulsoup 库作为一个方便的 html 解析器,将所有需要的数据提取到 pandas dataframe 中。

语言检测

这部分非常简单——我使用了一个快速的 langdetect 实现作为语言检测器,并在默认情况下用标题来加速检测——对平均长度大 10 倍的文本的语言检测速度较慢。

语言检测块

这一步每 1000 个文件用了 12 秒和 T5,检测准确率超过 99%。结果数据如下所示:

带有检测到的语言的新闻示例(仅标题)

文本预处理逻辑

文本矢量化逻辑是参赛者必须提出的核心算法决策之一。我们有一个足够大和足够多样化的约 100 万篇文章的语料库来使用预训练的单词嵌入,而不是基本的 TF-IDF 方法。但是首先我们必须执行通用的标记化和词干化过程。我使用了来自 nltk 库的停用词表和波特斯特梅尔。

标记化功能

在这一步之后,每个文本都由单词标记列表来表示。

下一步是用一个来自预先训练的语言模型的向量替换列表中的每个标记,该模型是 Glovefasttext

这个操作的结果是,每个文本现在都由一个语义丰富的单词向量列表来表示。我对列表的最大长度做了限制——50 个单词,标题与文章正文的开头连接在一起。

为了均衡所有向量的长度,已经执行了填充操作。我们现在已经得到了文本语料库的特征张量,每一行都代表了所选维度的一系列预先训练的词向量。

使用预训练单词嵌入的矢量化功能

深度神经网络架构

因为我们有足够的数据来进行神经网络训练,所以我决定使用深度神经网络(DNN)分类器来区分新闻和非新闻,并定义新闻主题。

考虑到这是一场算法竞赛,最大限度提高解决方案准确性的一个显而易见的选择是 SOTA NLP 模型架构,即某种大型转换器,如 BERT,但正如我之前提到的,这种模型太大太慢,无法通过对硬件和文本处理速度的限制。另一个缺点是,这种模型的培训需要几天时间,让我没有时间对模型进行微调。

我必须想出一个更简单的架构,所以我实现了一个轻量级的神经网络,它有一个 RNN (LSTM)层,接受代表文本的单词嵌入序列,并在其上有一个注意力层。网络的输出应该是类别概率(用于新闻过滤步骤的二元分类和用于新闻类别检测的多元分类),因此网络的上部由一组完全连接的层组成。

新闻话题检测——多类分类

我将使用多类分类器(检测新闻类别)作为例子来解释特定 DNN 体系结构的选择,因为它的目标更具挑战性,并且它的性能独立于在二元分类器中用来划分正负类之间的界限的阈值。

我们的神经网络的上部由三个密集层组成,输出层具有 7 个单元,对应于具有 softmax 激活函数和分类交叉熵作为损失函数的类的数量。

为了训练它,我使用了 News_Category_Dataset 并应用了映射逻辑,以便将最初的 31 个新闻类别归入 7 个类别之一:社会、经济、体育、科技、娱乐、科学和其他,以符合竞赛目标。

DNN 模型架构——多类分类器

我想分享神经网络模型的架构选择和超参数微调背后的逻辑。

  1. 漏失层增加了我们模型的泛化能力,并防止它过度拟合(漏失层在训练阶段的每次更新中随机地将给定百分比的权重归零)。学习曲线清楚地表明了模型在没有脱落层的情况下的过度拟合-训练集的精度增长到 95%,而验证数据集的精度在训练期间几乎没有增长。
  2. 应用于 LSTM 注意力结构顶部的批次标准化层在每批脱落后标准化激活,使激活平均值接近 0,激活标准偏差接近 1。它有助于提高较大批量的测试精度,降低较小批量的测试精度。
  3. 应用于密集层的正则化惩罚层参数的极值。
  4. 批量选择也会影响一个型号的性能。大小越大,模型训练越快,每步计算的梯度向量就越精确。这导致噪声减少,这使得模型更容易收敛到局部最小值,因此批量大小的选择通常是速度、内存消耗和模型性能之间的折衷。32 到 256 之间的值是常见的选择,在我们的例子中,模型显示出最高的精确度,批量大小为 64。将批量增加到 512 或 1024 会显著降低模型的准确性(相应地降低 2%和 4%)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

模型的性能取决于架构和超参数

注意力层解释

关于我们模型中使用的注意机制,有必要说几句话。Raffel 等人的 arXiv 论文中描述了所用方法背后的理论这是一个简化的前馈神经网络注意力模型,用于解决长序列的 RNN 信息流问题。特别地,注意层提供了到完全连接层的最佳过渡,创建了上下文向量(输入单词向量序列的嵌入)作为输入序列的隐藏状态的加权平均,其中权重表示序列元素的重要性。显式符号如下所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其中 T 是序列的长度, a 是可学习的函数,即具有双曲正切激活函数的单隐层前馈网络,与全局模型共同训练。

注意层类,由 Kaggle 上的 qqgeogor 实现

该模型的缺点是它没有考虑输入序列中的顺序,但是对于我们的新闻标题矢量化任务来说,这并不像一些序列到序列问题(如短语翻译)那样重要。

有许多出版物描述了为序列到序列问题设计的更复杂的注意力机制,一个好的开始是谷歌研究院的带注意力的神经机器翻译教程,我也建议检查一下注意力?立正!Lilian Weng 的文章,概述了注意力机制及其进化,并确保您已经阅读了最初的 Bahdanau 等人,2015 年的论文。在后变形金刚时代最新的 LSTM +注意力论文中,我推荐阅读斯蒂芬·梅里蒂最近的单头注意力 RNN ,这证明了巨大的变形金刚不是唯一可能的方法。

另外,不要忘记在 Attention 类中添加一个保存配置,因为它可以无缝加载带有自定义层的保存模型。

另一种更简单的向致密层的过渡是用于张量整形的平坦层。

其他想法&结果

为了提高分类器质量,可以利用提供的其他信息,如出版物来源和作者,并将它们作为一次性编码特征添加到网络中,但竞赛规则明确表示,将在具有其他特定来源列表的不同数据集上评估算法。

为了加快训练阶段的速度,我在装有 TeslaK80 GPU 运行时的 colab 笔记本上运行了代码。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

典型模型的学习曲线

具有新闻类别和二元新闻/非新闻分数的新闻样本

每 1000 个文本的分类步骤需要 13 秒。

新闻/非新闻—二元分类

为了从给定的出版物数据集中只过滤新闻,我们必须实现一个二元分类器。实际上这一步在新闻分类之前就已经完成了。二进制分类器具有非常相似的架构,但在输出层有明显的不同——最后一层有 1 个单元,具有 sigmoid 激活函数和二进制交叉熵作为损失函数。

竞赛组织者提供的数据包含了超过 90%的新闻出版物,所以我决定按来源(卫报、彭博、CNN 等)过滤 100%的新闻,然后用一个英文 Wikipedia atricles (好的,非宣传性的)来代表非新闻类。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

典型二元模型的学习曲线

训练的二进制分类器模型输出对象是正(1)类的概率,为了解释这些概率,我们需要施加阈值,该阈值将区分新闻和非新闻。在对边缘病例进行了长时间的仔细观察之后,这种选择已经被手动执行。

每 1000 个文本的分类步骤需要 14 秒。分类结果可以在上表中观察到。

将新闻按线索分组

新闻分组任务通过在文本嵌入向量上构建球树来解决,然后在每组邻居内使用由归一化 Levenshtein 距离控制的自适应搜索半径来搜索该树。

文本矢量化

为了对新闻进行分组,我们首先应该在数据集上引入某种度量。因为我们使用 DNNs 进行文本处理和分类,所以获得文本向量的最明显的方法是通过将文本传递通过预训练的多类 DNN 而获得嵌入,而不需要最后一层。我使用 128 单位的密集层作为输出来创建文本嵌入向量。

后来我发现这种方法并不是最佳方法——当我切换到更简单的 TF-IDF 矢量化时,新闻分组的性能明显提高。这是很容易解释的——在新闻分类中,我们需要概括整个新闻的语义,而不管特定的政治家的名字、小工具、名人,甚至特定的环境,馈送给 DNN 的预训练单词向量的序列是这项任务的合适选择。在新闻分组中,我们处理不同的设置——每个线程应该描述一个非常特殊的事件——某人发生了一些事情,人物姓名,甚至动词和状语修饰语在整个新闻线程中应该是相同的,因此经典的 TF-IDF(计算每个文本中 n 元语法频率的向量,通过整个语料库中的 n 元语法频率归一化)是丢失较少有价值信息的方法。为了对抗 TF-IDF 输出矩阵的稀疏性,我应用了 SVD 分解,将每个文本的向量压缩到选定的维度(在我们的例子中是 1000)。

在 tom 上使用 SVD 对 TF-IDF 文本进行矢量化,以创建密集嵌入

快速最近邻搜索

下一步实际上是文本分组。这项任务的基本无监督方法是聚类,但由于我们不可能从第一次拍摄就获得完美的聚类,我们的算法需要迭代方法,并且整个数据集的聚类是昂贵的。此外,我们没有适当的分组质量指标来运行自动超参数调整程序,我们也不应该手动调整它们,因为我们很可能需要不同的超参数集来用于不同的新闻数据集。

我决定**一个更精确和灵活的方法是在嵌入向量的文本上构建一个快速搜索索引,然后查询这个索引。**快速高维最近邻搜索的有效方法是在文本嵌入上建立二叉空间划分树,即球树KD 树(k 维二叉查找树是一种用于组织 k 维空间中的点的空间划分数据结构),而无需显式计算数据集中的所有距离。这些算法的树构造复杂度为 O(N log(N))。我特别选择了一个 scikit-learn Ball Tree 实现,因为它比高维 KD 树具有更低的查询时间 O(D log(N)),并且我们必须优化查询时间,因为我们打算对数据集中的每个元素使用不同的搜索半径来执行迭代搜索。有关 KDtree 和 BallTree 数据结构之间的差异以及性能基准测试的更多详细信息,请参考 scikit-learn 撰稿人 Jake VanderPlasof 的这篇精彩文章。

初始化树结构

分组算法

好了,现在我们终于有了所有论文的索引,可以根据不同样本之间的距离将它们分组。

我没有足够的时间来思考一些更复杂的方法,只是在每个新闻类别中的所有论文上创建了一个循环**(我们可以利用前面的算法步骤来按新闻类别划分我们的数据集)检查它们在半径 r_start 中的邻居,r_start 是根据经验选择的,它使用的论文比实际包含的线程多一点。然后我计算了一个经验泛函 variative _ criterium _ norm——组内文本之间的归一化 Levenstein 距离——并迭代地减小搜索半径 r_curr,直到这个泛函变得小于经验发现的约束或搜索半径大小达到 r_min 约束。如果在给定区域没有新闻,我增加搜索半径,直到我们找到一些邻居,然后切换到半径递减分支。这种迭代搜索背后的想法是,相似的新闻有部分相似的标题(一些实体,如主语、宾语,有时动词,在新闻线索中是不变的)。从代码片段中可以更容易地看到所开发算法的其他细节。**

新闻分组逻辑

有 7 个超参数控制算法:r_min,r _ max 这些参数控制查询区域的大小,r_start,r _ step 控制查询区域动态,vc_min,vc_max,delta_max,控制组内归一化 Levenshtein 距离的值-这定义了组中新闻标题的方差。在选择了最终的文本嵌入大小(SVD 中的 n_components)和使用的 n_grams 范围(我使用了 1-3)等矢量化参数之后,应该对这些超参数进行调整。

我们如何估计一个“适当的”新闻分组并不是很明显,所以在我得到一个合理的分组结果后,我没有花太多时间玩超参数——我的意图是提出一个工作方法来解决给定约束条件下的问题。显然,还有一些改进的空间,比如检查我们是否可以合并一些组,以及过滤掉一些偶然的噪音。实际上,为了获得合理的组数量和密度,分组超参数应该针对每个数据集进行微调,因此可以有一个外部循环来对它们实施一种随机搜索。了解我们得到的特定分组并评估其质量的方法之一是检查分组大小分布直方图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在测试数据集上获得的组大小分布直方图

事实上,所描述的方法可以被看作是 DBSCAN 集群的一个相关部分。

执行时间取决于为数据集选择的超参数和数据结构,典型值从 8.5 秒/ 1000 张纸到 25 秒/ 1000 张纸,包括由昂贵的 SVD 操作定义的矢量化时间。

很抱歉列出了这么长的列表,这里是按团体大小排列的社会类别的全部结果

分组结果—前 3 组(按大小排序)

线程排名

实际上,有很多功能可以用于线程排名,问题是这个排名可能非常主观,取决于特定用户的地区和兴趣。我们可能同意存在全球和本地新闻,对政治问题的一些猜测/意见和纯粹的事实,但关于 Trump 弹劾听证会的一些新信息可能比全球但遥远的悲剧更重要,如澳大利亚的森林火灾新闻的重要性是一种感知,而不是一种客观特征

鉴于上述情况,我将仅描述处理该任务的方法:线程排名的最明显特征是线程中的出版物数量、来源的排名(在社会类别中,例如彭博和金融时报是最受尊敬的来源)以及线程的语义,如“国际政治”、“全球经济”、“战争”、“全球事故”、“本地事故/犯罪”等。这种方法预先假定了这些语义类别的手动引入和排序以及新闻源的手动排序。如果我们不想手动创建特征,另一种可能的方法是手动对线程的一些选择进行排序,计算它们的平均语义向量(对我们使用的任何矢量化进行平均),然后使用这些向量作为预测器,根据我们分配给线程重要性的分数训练出排序模型(一种回归模型)。

事实上,只要按照大小对线程进行排序,就可以获得相当合理的排名。鉴于这一事实以及我在比赛期间没有实现排名部分的事实,我将让那些热情的人自由尝试上面讨论的任何方法。

一个重要的问题是,竞赛预先假定了一个静态排名——我们得到了同一日期的所有新闻,但实际上新闻是时间相关的,并且失去了它们的新颖性久而久之,这种相关性的衰减可以用 exp(-t)函数来描述。

感谢你阅读这篇长文,我希望你已经找到了一些可以在你的 NLP 项目中使用的想法。

用 TensorFlow 构建一个热编码层

原文:https://towardsdatascience.com/building-a-one-hot-encoding-layer-with-tensorflow-f907d686bf39?source=collection_archive---------10-----------------------

如何在 TensorFlow 中创建自定义神经网络图层以对分类输入要素进行热编码

一个热编码是机器学习模型预处理分类特征的一种常见方式。这种类型的编码为每个可能的类别创建一个新的二进制特征,并将值 1 分配给对应于其原始类别的每个样本的特征。这在视觉上更容易理解:在下面的例子中,我们对一个由三个类别组成的颜色特征进行了热编码(红色绿色蓝色)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一个简单分类特征的热编码(作者图片)

Sci-kit Learn 提供了开箱即用的OneHotEncoder类,使用一个热编码来处理分类输入。简单地创建一个sklearn.preprocessing.OneHotEncoder的实例,然后将编码器安装到输入数据上(这是一个热编码器识别数据帧中可能的类别并更新一些内部状态,允许它将每个类别映射到一个唯一的二进制特征),最后,调用one_hot_encoder.transform()对输入数据帧进行一次热编码。关于OneHotEncoder类的伟大之处在于,一旦它适合输入特征,您可以继续向它传递新的样本,它将一致地对分类特征进行编码。

一个使用 Sci-kit Learn 的 OneHotEncoder 的热编码

张量流模型的一种热编码

最近,我正在处理一些作为输入传递给张量流模型的分类特征,所以我决定尝试找到一种热编码的“张量流原生”方式。

经过大量的搜索,我发现了两个建议:

用 Sci-kit Learn 的 OneHotEncoder 就行了

我们已经知道它是可行的,一个热编码就是一个热编码,所以为什么还要用 TensorFlow 来做呢?

虽然这是一个有效的建议,对于简单的示例和演示非常有效,但是在您计划将您的模型作为服务进行部署以便它可以在生产环境中执行推理的场景中,它可能会导致一些复杂性。

退一步说,将OneHotEncoder与 Sci-kit Learn 模型结合使用的一大好处是,您可以将它与模型本身一起作为 Sci-kit Learn 管道中的一个步骤,本质上是将一个热编码(可能还有其他预处理)逻辑和推理逻辑捆绑为一个可部署的工件。

具有一个热编码和预测逻辑的 Sci-kit 学习流水线

回到我们的 TensorFlow 场景:如果您要使用OneHotEncoder来预处理 TensorFlow 模型的输入特征,您将需要处理一些额外的复杂性,因为您必须:

  1. 在模型用于推理的任何地方复制一个热编码逻辑。
  2. 或者,将 fit OneHotEncoder和经过训练的 TensorFlow 模型作为单独的构件进行部署,然后确保它们被正确使用,并由使用该模型的所有应用程序保持同步。

使用 tf.one_hot 操作

这是我遇到的另一个建议。tf.one_hot操作接受一个类别索引列表和一个深度(对于我们的目的,本质上是一些独特的类别),并输出一个热编码张量。

tf.one_hot 操作

在上面的例子中,你会注意到OneHotEncodertf.one_hot之间的一些关键区别。

  • 首先,tf.one_hot只是一个操作,所以我们需要创建一个使用这个操作的神经网络层,以便将一个热编码逻辑包含到实际的模型预测逻辑中。
  • 其次,不是传入字符串类别(红色蓝色绿色),而是传入一个整数列表。这是因为tf.one_hot不接受类别本身,而是接受一个热编码特征的索引列表(注意,类别索引 0 映射到 1×3 列表,其中列 0 的值为 1,其他的值为 0)
  • 第三,我们必须传入一个唯一的类别计数(或深度 ) *。*该值决定了生成的一个热编码张量中的列数。

因此,为了将一个热编码逻辑作为 TensorFlow 模型的一部分,我们需要创建一个自定义层,将字符串类别转换为类别索引,确定输入数据中唯一类别的数量,然后使用tf.one_hot操作对分类特征进行热编码。我们接下来会做所有这些。

创建自定义层

首要任务是以一致的方式将字符串类别转换为整数索引(例如,字符串蓝色应该总是被转换为相同的索引)。

输入文本矢量化

实验性的TextVectorization层可用于标准化和标记化字符串序列,例如句子,但是对于我们的用例,我们将简单地将单个字符串类别转换成整数索引。

使文本矢量化图层适应颜色类别

我们在创建层时指定了output_sequence_length=1,因为我们只希望传递到层中的每个类别有一个整数索引。调用adapt()方法使图层适合数据集,类似于对OneHotEncoder调用fit()。在层被拟合之后,它维护唯一类别的内部词汇表,并将它们一致地映射到整数索引。你可以通过调用get_vocabulary()来查看图层的词汇。

OneHotEncodingLayer 类

最后,我们现在可以创建一个类来表示神经网络中的一个热编码层。

一个热编码分类要素的自定义图层类

该类继承自PreprocessingLayer,因此它继承了基础adapt()方法。当这个层被初始化时,TextVectorization层也被初始化,当adapt()被调用时,TextVectorization被拟合到输入数据,并且两个类属性被设置:

  • **self.depth** 是输入数据中唯一的类别数。该值在调用tf.one_hot时使用,以确定结果二进制特征的数量。
  • **self.minimum**TextVectorization层输出的最小指标。从运行时的索引中减去该值,以确保传递给tf.one_hot的索引落在范围[0,self.depth-1]内(例如,如果TextVectorization输出范围[2,4]内的值,我们将从每个值中减去 2,以便得到的索引在范围[0,2]内)。

get_config()方法允许 TensorFlow 在模型保存到磁盘时保存层的状态。当模型加载到内存中时,来自层配置的值将被传递给层的__init__()方法。注意,每当这些值被传入时,我们都显式地设置了词汇、深度和最小值。

使用自定义图层

现在我们可以在一个简单的神经网络中尝试新的层。

具有一个热编码的简单神经网络

这个简单的网络只接受分类输入,对其进行热编码,然后将热编码的要素与数字输入要素连接起来。注意,我在 DataFrame 中添加了一个数字列id,以说明如何将分类输入从数字输入中分离出来。

就是这样!我们现在有一个工作的神经网络层,可以热编码分类特征!我们还可以将这个模型保存为 JSON 配置文件,部署它,并将其重新加载到内存中以执行推理。请注意,它以与之前相同的方式对颜色类别进行了热编码,因此我们知道我们模型的后续层将按照它们在训练期间出现的相同顺序提供相同的功能。

从配置中保存和加载模型

你可以在这里找到包含所有代码示例的笔记本:https://github . com/gnovack/TF-one-hot-encoder/blob/master/onehotencoderlayer . ipynb

感谢阅读!欢迎在下面留下任何问题或评论。

参考

用 PyTorch 构建一次性学习网络

原文:https://towardsdatascience.com/building-a-one-shot-learning-network-with-pytorch-d1c3a5fafa4a?source=collection_archive---------4-----------------------

我们如何用每门课这么少的样本建立一个深度网络?

近年来,深度学习由于其高性能而在图像识别和分类任务中非常流行。然而,传统的深度学习方法通常需要一个大型数据集来训练模型,以区分非常少的不同类别,这与人类能够从甚至非常少的例子中学习的方式截然不同。

少击或单击学习是一个分类问题,旨在只给定有限数量的样本对对象进行分类,最终目标是创建一个更像人类的学习算法。在本文中,我们将通过使用一种特殊的网络结构:暹罗网络,深入研究解决一次性学习问题的深度学习方法。我们将使用 PyTorch 构建网络,并在 Omniglot 手写字符数据集上对其进行测试,并使用一次性学习评估指标进行多次实验,以比较不同网络结构和超参数的结果。

Omniglot 数据集

Omniglot 手写字符数据集是由 Lake 等人提出的用于一次性学习的数据集。它包含来自 50 个不同系列的字母表的 1623 个不同的手写字符,其中每个字符由 20 个不同的人手写。每个图像的大小为 105x105 像素。这 50 个字母被分成 30:20 的比例进行训练和测试,这意味着测试集是在一个全新的字符集上,这是以前从未见过的。

计算环境

训练和实验完全是通过谷歌实验室完成的,使用了包括特斯拉 K80 和 P100 在内的一系列 GPU。我们使用的库包括 Numpy、Matplotlib 和 PyTorch。

方法

传统的深度网络通常不能很好地进行一次或几次学习,因为每类很少的样本很可能导致过拟合。为了防止过度拟合问题,并将其扩展到看不见的字符,我们建议使用暹罗网络。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图一。卷积暹罗网络架构

图一。是卷积暹罗网络的主干架构。与传统的 CNN 采用 1 幅图像的输入来生成暗示图像所属类别的独热向量不同,Siamese 网络采用 2 幅图像并将它们馈入具有相同结构的 2 个 CNN。输出将被合并在一起,在这种情况下是通过它们的绝对差异,并被馈送到完全连接的层,以输出一个表示两幅图像相似性的数字。数字越大,表示两幅图像越相似。

暹罗网络不是学习哪个图像属于哪个类,而是学习如何确定两个图像之间的“相似性”。在训练之后,给定一个全新的图像,网络然后可以将该图像与来自每个类别的图像进行比较,并确定哪个类别与给定的图像最相似。

数据集预处理和生成

训练和验证数据加载器

为了训练暹罗网络,我们必须首先生成适当的输入(成对)并为模型定义基础事实标签。

我们首先定义两幅图像,它们来自相同字母表中的相同字符,相似度为 1,否则为 0,如图 3 所示。然后,我们根据 dataloader 迭代中索引的奇偶性,随机选择一对图像输入到网络中。换句话说,如果当前迭代是奇数,我们从同一个字符检索一对图像,反之亦然。这确保了我们的训练数据集对于两种类型的输出都是平衡的。两幅图像经历相同的图像转换,因为目标是确定两幅图像的相似性,所以将它们输入不同的图像转换没有意义。

以下是生成训练集的代码:

我们创建了 10000 对这些数据作为我们的训练集,然后以 80:20 的比例随机地进一步分成训练和验证。

测试加载器

对网络在一次学习中的性能的评估可以通过 n 路一次学习评估度量来完成,其中我们找到代表 n 个类别的 n 个图像和属于 n 个类别之一的一个主图像。对于我们的暹罗网络,我们计算了主图像相对于所有 n 个图像的相似性,具有最高相似性的对意味着主图像属于该类。

测试加载器是以支持上述评估的方式构建的,其中随机获取了一个主图像,并且还检索了代表 n 个类别的 n 个图像,其中一个图像来自主图像的相同类别。

以下是用于生成测试集的代码:

对于我们的最终测试,我们将我们的网络扩展到 4 路一次性学习,测试集大小为 1000,20 路为 200。

实验

实验一。用于一次性学习的传统连体网络

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图二。Koch 等人的暹罗网络架构。

暹罗网络的主要部分是前面显示的双卷积架构。我们将尝试构建的第一个卷积架构来自 Koch 等人的论文“用于一次性图像识别的暹罗神经网络”,如图 2 所示。需要注意的一点是,在展平之后,两个卷积分支之间的绝对差异被馈送到全连接层,而不仅仅是一个图像的输入。

PyTorch 中的网络构建如下:

我们可以使用以下功能进行培训:

超参数设置

批量大小:因为我们正在学习两幅图像有多相似,所以批量大小需要相当大,以便模型具有通用性,特别是对于像这样有许多不同类别的数据集。因此,我们使用的批量大小为 128。

学习率:我们测试了从 0.001 到 0.0005 的几个学习率,并选择了 0.0006,它提供了最好的损失减少率。

优化器和损耗:我们采用传统的 Adam 优化器来优化该网络,并使用 logits 进行二进制交叉熵(BCE)损耗。

结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 3。原始网络的训练和验证损失

该网络被训练 30 个时期。图 3。是每个时期后的训练和验证损失图,正如我们所看到的,它显示了接近结束时的急剧下降和收敛。验证损失通常随着训练损失而减少,表明在整个训练中没有发生过拟合。在训练期间,将保存具有最低验证损失的模型。我们使用验证损失而不是训练损失,因为它是模型不仅仅对训练集表现良好的指标,这可能是过度拟合的情况。

实验二。添加批量标准化

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4。使用 BatchNorm 的模型架构

为了进一步改善网络,我们可以添加批量标准化,这应该会使收敛过程更快、更稳定。图 4 是更新后的架构,每个卷积层后都有一个 BatchNorm2d。

结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 4。10 个时期后的训练结果

正如预期的那样,与原始网络相比,训练损失和验证损失都下降得更快。有了更好的结果,我们决定也训练模型更多的时期,看看它是否会比实验 1 表现得更好。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 5。50 个时期后的训练结果

如损失图所示,结果略好于实验 1 的原始结果。由于损失在 40 和 50 代之间缓慢收敛,我们在第 50 代停止了训练。这是目前我们取得的最好成绩。

实验三。用轻型 VGG16 替换 ConvNet

在让原来的网络工作得相当好之后,我们还可以为我们的暹罗网络测试不同的成熟 CNN,看看我们是否能取得更好的结果。对于 105x105 的小图像大小,我们希望使用一个相对较小的网络,没有太多的层,但仍能产生不错的结果,因此我们借用了 VGG16 的网络架构。

最初的 VGG16 对于我们的尺寸来说仍然有点太大,其中最后 5 个卷积层只处理单个像素,所以我们取消了它们,最终网络如下:

结果

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 6。VGG16 暹罗网络结果

如损失图所示,训练损失比先前的实验下降得慢得多。这可能是由于卷积层的内核大小相当小(3×3),这给出了小的感受域。对于计算两个图像之间的相似性的问题,查看两个图像的“更大画面”而不是关注小细节可能是有益的,因此在原始网络中提出的更大的感受野工作得更好。

对模型的评价

评估网络的代码实现如下:

四路一次学习

我们首先使用一组全新的图像来测试 4 向一次学习进行评估,其中所有的测试图像在训练期间都没有使用,并且模型也不知道任何角色。结果显示大约 90%的准确率,这表明该模型可以很好地推广到看不见的数据集和类别,实现了我们在 Omniglot 数据集上一次性学习的目标。

20 路一次学习

之后,我们对 200 组进行了 20 路单镜头学习评估。结果仍然是 86%左右。我们将结果与 Lake 等人提供的基线进行了比较:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

虽然我们没有超过或复制论文提出的 92%的准确性(可能是由于细节,如变化的层学习率),但我们实际上非常接近它。

此外,我们的模型实际上比许多其他模型表现得更好,包括正常的暹罗网络和最近邻居。

结论

所以你有它!这就是如何为一次性学习 Omniglot 数据集构建卷积暹罗网络。完整代码也发布在 Github 的以下目录中:

[## ttchengab/One_Shot_Pytorch

设置:从 https://github.com/brendenlake/omniglot 下载数据集,转到/python 并提取文件夹…

github.com](https://github.com/ttchengab/One_Shot_Pytorch.git)

感谢您坚持到现在🙏!我将发布更多关于计算机视觉/深度学习不同领域的文章,请务必查看我关于 3D 重建的另一篇文章!

构建个性化的实时时装收藏推荐系统

原文:https://towardsdatascience.com/building-a-personalized-real-time-fashion-collection-recommender-22dc90c150cb?source=collection_archive---------9-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们的 RecSys 选择的服装。左:“任何有短裤的东西”。右上:“都市生活方式”。右下:《如花梦》。服装从 DeepFashion、开源由 刘 z 等人

现代 RecSys

我们将利用 PyTorch 中的迁移学习、近似最近邻和嵌入质心检测来构建我们的推荐器。

我在数据行业工作了七年多,有幸设计、构建和部署了两个推荐系统(RecSys ),并为数百万客户提供了服务。在本文中,我将通过一个带有代码的案例研究来介绍可视化推荐器的现代方法,并分享我设计 RecSys 的一些经验。

商业问题

假设你被一家电子商务公司**happy panda co .**聘用,负责他们的时装系列专题。产品团队概述了需求:

  • 该模型应该能够扫描所有 280,000+产品图像,并自动生成一组推荐产品,这些产品是根据用户查看/购买的内容定制的。
  • 速度对电子商务公司至关重要。该组推荐的产品应在 2ms 内生成,以满足管道的“实时”要求。
  • 该模型应该能够适应新产品和新用户。

听起来很有趣?让我们建造这个。

数据

我们将使用**【deep fashion】的子集数据,由香港中文大学刘 z 等人开源,。我们的数据由 46 个类别的 28 万张时尚图片组成。你可以从他们的网站上下载数据。**

此外,该团队发布了一个更新版本,增加了的数据。你需要填写谷歌表格来获取数据。

时尚系列——打造时尚系列的步骤

了解业务需求后,我们分解构建时装系列所需的步骤:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我打造时装系列的步骤

回顾时尚系列的成果

在深入研究代码之前,让我们看一下模型的输入/输出,以获得对问题和建议的解决方案的直觉。

输入:

  • 我和妻子商量,通过选择 12 套风格相似的服装来模拟用户查看/购买的服装。我们将关注三个用户:爱丽丝、贝拉和凯茜,每个人都有不同的口味偏好。
  • ****我只会将图像传递到模型中;模型不吸收额外的属性/描述/产品细节;这是因为我们希望模型能够自动学习和检测传递给它的时尚图像的风格,而无需进一步的人/机器标记。

输出:

  • 目标是让模型生成的集合包含输入的某些方面,但给用户带来惊喜。****

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

左图:爱丽丝查看/购买的服装。右图:时装系列推荐的服装。 DeepFashion、开源 刘 z 等人

A lice 来自阳光明媚的新加坡,喜欢短裤和适合炎热天气的服装。根据她选择的服装,《时尚收藏》推荐了各种各样的新服装,都配有短裤,看起来非常适合阳光下的生活。请注意,我们没有明确告诉模特根据短裤过滤服装。 Fashion Collection 可以捕捉到爱丽丝喜欢搭配短裤的服装的信号,并做出适当的推荐。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

左图:Bella 查看/购买的服装。右图:时装系列推荐的服装。服装来自 DeepFashion,开源来自 刘等

ella 来自艺术巴塞罗那,喜欢她衣服上的花卉图案和多彩设计。根据她选择的服装,时装系列推荐了各种颜色、图案和剪裁与她品味相似的各种新服装。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

左图:Cathy 查看/购买的服装。右图:时装系列推荐的服装。服装从 DeepFashion、开源由 刘 z 等人

阿西来自国际化的纽约,穿着她熟悉的都市生活方式。城市生活方式是一种更复杂的风格**,分层、破牛仔裤、配饰、靴子、图案 t 恤是其中一些定义特征。因此,令人兴奋的是,时装系列已经认识到了这种风格的一些关键特征,并推荐了带有层次感的服装组合,破旧的牛仔裤,配饰,靴子,图案 t 恤!**

这也是为什么我们不想硬编码过滤器/按类别限制服装。我们希望时装系列能够响应不同用户和不断变化的用户偏好,即使是像城市生活方式这样多样化的风格。

在下一节中,我将介绍一些主要的概念和代码片段。如果你不能在一次阅读中理解所有的理论,不要担心。本质上,这些都是不断发展的密集话题;我将提供一个高层次的概述,并在进一步的阅读材料中提供额外的资源供您参考。我们将遵循上面流程中概述的阶段(数据、准备、建模、搜索)。

代码

链接到 Colab 上的代码 (你只需要一个免费的 Google 账号就可以在云端的 GPU 上运行代码)。我们将使用杰瑞米·霍华德的 Fastai 包(以及著名的程序员实用深度学习课程)。

[Prep]代码:将图像转换为嵌入内容

这种转换是我们模型数据处理的第一步:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们代码的结果——图像数据已处理,可用于训练。各路服装来自 DeepFashion,开源作者 刘 z 等人

理论:什么是嵌入,为什么我们需要它们?

为了解释和说明嵌入的概念,我们将使用一个更小的开源数据集,称为 FashionMNIST

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传****外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

左图:示例图像。右图:图像的数字表示。样本图片来自时尚达人,开源作者小 H.

传统上,我们将图像表示为一个巨大的整数数组(RGB 图像的 3D 数组和灰度图像的 1D 数组)。这些阵列非常庞大,并且呈指数级增长,我们将需要跟踪数百万个数字来分析数百张高分辨率图像!使用整数数组来缩放任何建模都是不可能的;因此,现代的嵌入方法产生了。

嵌入概念的一个很好的例子是 Will Koehrsen 的“神经网络嵌入解释”中的例子。传统上,我们利用像一键编码这样的技术来表示矩阵中的项目。例如,给定三本书,我们将有一个 3x3 矩阵,其中每一项都由一个由个离散的个数字组成的数组表示,这个数组的随着每增加一个新项而增长** (4x4 有 4 项,5x5 有 5 项……)。此外,没有相似感,也没有距离感,因为这些项目没有以任何形式的关系连接。**

**# One Hot Encoding Categoricals****books = ["Harry Potter and The Philosopher's Stone",
         "Harry Potter and The Chamber of Secrets", 
         "The Lean Startup"]****books_encoded = [[1, 0, 0],
                 [0, 1, 0],
                 [0, 0, 1]]****Similarity (dot product) between First and Second = 0
Similarity (dot product) between Second and Third = 0
Similarity (dot product) between First and Third = 0**

一旦我们应用一个转换来将对象转换成嵌入,我们现在在一个连续的尺度上限制表示每个项目的数组中的元素数量(在这个例子中限制为 2),并且这些值具有基于关系的含义。基于相似性(点积)彼此接近的对象是高度相关的。

**# Idealized Representation of Embedding****books = ["Harry Potter and The Philosopher's Stone",
         "Harry Potter and The Chamber of Secrets", 
         "The Lean Startup"]****books_encoded_ideal = [[0.53,  0.85],
                       [0.60,  0.80],
                       [-0.78, -0.62]]****Similarity (dot product) between First and Second = 0.99
Similarity (dot product) between Second and Third = -0.94
Similarity (dot product) between First and Third = -0.97**

嵌入是离散变量的矢量表示,而不是分析每幅图像的数百万个离散变量。对于深度学习,我们通常利用神经网络嵌入来将分类变量的维度降低到可管理的程度

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

时尚嵌入投影。图片来自 FashionMNIST,【Tensorboard 动画

因为我们可以控制向量表示的大小,所以我们可以将一个巨大的图像数组缩小成一个由更少的数字组成的小向量。从上图中的 FashionMNIST 对象可以看出结果,其中对象被投影到 3D 矢量空间。通过嵌入过程,相似的个图像向量将被投影成彼此靠近**。因此,当我们有嵌入时,我们可以将对象投影到向量空间中,并为可视化和简单的推荐制定距离和邻居的概念。你可以用下面的笔记本探索嵌入的概念,并在进一步阅读部分阅读更多相关内容:**

[模型]代码:深度卷积神经网络(CNN)的迁移学习

Fastai 在建立神经网络的过程中抽象出了许多代码逻辑,允许我们用几行代码进行迁移学习:

用 Fastai 钩子运行模型并为每个图像生成嵌入也相当简单。注意,我们想要倒数第二个线性层,因为该层存储图像表示的嵌入;最后一个线性图层是输出图层。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

请注意,倒数第二个线性图层是(4)

因此,当我们调用 learner.model.module[1][4]来获得具有嵌入的层时。

【模式】理论:CNN 是什么?

卷积并不是一项新技术。本质上,我们正在对图像中的每个像素应用内核以实现一个目标,通常是模糊、锐化或检测边缘/对象。对于每个像素,我们将用内核做一个元素级的乘积,然后将结果相加得到一个单一的数。

由于每个内核专门检测图像的一个方面,你可以想象我们将不同的内核堆叠起来,制定一个全面的策略。事实的确如此,内核的集合被称为过滤器。在 CNN 中,我们甚至可以堆叠多层过滤器,每个过滤器都有特定的任务。

如果你有兴趣,你应该试试 Victor Powell 开发的带有不同类型内核的工具。CNN 是一个迷人的模型,因为它结合了卷积和神经网络的力量。有许多不同的架构,但通常由卷积、子采样、激活和完全连接的组合组成,如 Algobeans 所述。你可以在进一步阅读部分了解更多关于内核和 CNN 的知识。

【模型】理论:迁移学习是如何工作的?

对于大多数现实部署,我们不会从头开始训练 CNN。多年来,像微软研究院这样的组织已经发布了最先进的大规模预训练深度 CNN (DCNN)模型,我们应该通过在他们的基线模型上进行训练来利用他们的工作。这就是所谓的迁移学习。

具体来说,我们拿一个像 ResNet50 (He K .,et al.) 这样的大型预训练 DCNN,冻结大部分层,在最后几层上训练。直觉是,对于大多数 DCNN,过滤器获得的知识是可转移的成分,例如边缘、图案、梯度、颜色等的检测。因此,我们对问题的最后几层进行微调,就可以得到一个可行的解决方案。你可以在进一步阅读部分探索迁移学习的概念。

[搜索]代码:用骚扰和嵌入质心检测来近似最近邻居

我们遍历数据帧中的每一行,用 get_nss_by_vector 函数找到最近的邻居。

最后,我们将所有 12 个(或更多)用户选择的项目的嵌入插入到一个列表中,然后对每个维度的嵌入值进行平均**;这将创建一个虚拟对象,表示所有选定项目的聚合值。然后,我们可以找到这个虚拟物品的最近邻居,当我们调用 Annoy 时,这可以在大约 2ms 内完成。**

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

用 Shorts 集合生成任何内容所花费的时间

[搜索]理论:近似最近邻

如果我们只有一个小的图像搜索语料库,简单的距离度量,如余弦相似性将工作。在现实世界的部署中,比如电子商务,我们通常有数百万张图片需要相互比较,API 对每张图片进行成对比较是不切实际的。来自 Spotify 的 Erik Bernhardsson 编写了一个易于使用的 API,可以集成到我们的 PyTorch 工作流中。

更重要的是,它帮助我们找到最近的邻居,而不需要计算每张图像之间的成对距离。

如果你有兴趣了解更多关于 aroy 的知识,请在“进一步阅读”下查看 Erik 的文章。

我们学到了什么

我们将详细介绍构建个性化实时时装系列推荐器的整个过程。借助 PyTorch、Fastai 和 Annoy 等现代工具,我们构建了一个强大的产品,可以根据用户的风格生成相关的推荐。

进一步阅读

从 Google Sheets 构建一个精巧的 Dash 应用程序——第 1 部分

原文:https://towardsdatascience.com/building-a-plotly-dash-app-from-google-sheets-part-1-d37dc41ece10?source=collection_archive---------21-----------------------

Plotly Dash 应用程序

用众所周知的免费工具构建的非传统仪表板

介绍

对于大多数公司的数据科学家或分析师来说,仪表板仍然是一项非常受欢迎的任务。在一个完美的世界中,你可能会连接到大的数据流管道,或者用 OLAP 立方体建立一个单独的数据仓库,以提供一组复杂的实时分析。然后在应用程序中构建一些漂亮的灵活性,供业务团队用于即席号码。也许你甚至可以用一些预测算法来增加数据。听起来很有趣。这不是仪表板。

或者,有时您需要围绕现有的一组流程构建一个仪表板,并且尽量不干扰业务。那是仪表板着陆的地方。这个项目的想法是支持一个为虚构的自行车公司工作的现场营销团队。每个营销人员在全国各地举办实体活动,并需要向营销主管报告活动的类型、规模和频率。此外,这些指标需要跟踪员工奖金激励的进展。这家 T4 的大公司有很多繁文缛节,部门内部的部门和超负荷工作的 IT 团队。因此,营销负责人希望有一个非常简单的解决方案,可以在几天内完成,不需要召开 3 次预算会议来获得批准,也不需要培训现场员工使用新工具。

输入谷歌表单。我们将使用 Google 提供的简单、免费且非常熟悉的工具,而不是创建另一个生产级数据存储和 web 表单来接收数据。这允许团队快速调整和编辑数据。正如你将看到的,谷歌已经提供了一些他们用来构建 G-Suite 的相同代码,以制作我们自己的定制解决方案。

这个项目将分为 4 个部分。

  1. 使用 Google 和 code.gs 的设计和 ETL 过程
  2. 将 Google Sheets 数据连接到 python
  3. 打造一款 Plotly Dash app
  4. 在 Heroku 上主持[即将推出]

我们要建造这个:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们示例仪表板的图像——作者提供的图像。

第 1 部分——使用 Google 和 code.gs 进行设计和 E.T.L

在开始 dashboard 和任何应用程序之前,我们必须首先列出构建它所需的重要元素。

  1. 按日期过滤—我们希望允许营销主管查看特定日期范围内的活动
  2. 聚合值-每个事件都有一个可量化的特征,如在诊所接受教育的员工人数或测试骑过自行车的消费者人数
  3. 地图——每个事件的规模和位置的直观概述,以便团队识别覆盖范围的缺口
  4. 季度日期范围—现场营销团队按季度获得奖金,我们需要按此固定日期进行筛选

为了构建这个项目,我们将创建一个简单的谷歌表单。这对于调查和快速数据输入来说非常方便,并且允许数据验证,比如日期和所需的响应,就像典型的 SQL db 一样。此外,Google Forms 会将每个响应存储在一个 Google Sheet 中,作为我们 Dashboard 应用程序的数据源。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

谷歌表单样本——作者图片。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

带有表单响应的 Google 表单—作者图片

地理编码

在上述需求中,几乎所有需求都可以在 python 中以足够快的速度处理,以加载一个应用程序。然而,在 Plotly 中在地图上绘制坐标的最佳方法是使用纬度和经度。我不想让营销团队在提交 Google 表单时查找这些坐标,从而使数据输入变得复杂。在这一点上,我无法找到一个预建的 Google form 解决方案来整合地图,以实现“放下大头针”的解决方案(也许是我的下一个项目想法?).我发现将地址转换成坐标需要调用 python 中的 API,我想在我们的仪表板加载时避免这种情况。

然而,在 Google 产品套件中,有一个内部 API 可以快速调用经度和纬度。接下来,我们将在新的 Google Sheet 中编写一个脚本,将纬度和经度附加到 Google Sheet 中存储响应的最后一列。

code.gs

https://developers.google.com/apps-script/reference

Google Apps Script 是一个非常强大的工具,它允许您构建脚本函数来执行 Google 表单、文档或幻灯片中尚不存在的任务。code.gs 的名字是 Node.js 的巧妙变化,因为这种脚本语言与基于 javascript 的语言的外观和感觉有很多相似之处。该工具允许您将自定义函数写入您的电子表格(或任何 g-suite 工具!)可以像在工作表中使用=SUM(A1:A5)一样使用。或者您可以使用它连接到您的 Google 日历,在 Google 文档中查找会议请求。我不是用 javascript 写的,也没见过多少代码,但是对代码有一些基本的理解可以让你开始运行一些东西。

Sheets 脚本语言的核心建立在几个类上,如.sheet().range().cell(),这允许脚本在工作表中找到特定信息,进行一些转换,并将新值返回到另一个特定位置。正是我们要找的。

此外,还有很多很多类可以使用其他谷歌服务,比如地图类中的Maps.newGeocoder()。有了这个类,我们就可以在脚本中使用健壮的谷歌地图服务,从非常基本的城市或邮政编码数据中调用我们的纬度和经度。

因此,为了准备仪表板的数据,要完成的第一项任务是运行一个脚本来插入纬度和经度。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在带有表单响应的 Google 表单中,打开脚本编辑器——作者图片

在 Will Geary 的这篇教程的帮助下,我能够在 code.gs 中创建一个函数来查找地址并插入纬度/经度坐标:

如果您熟悉 javascript,您可能会找到更简洁或更好的方式来编写这些代码。在伪代码中,这个函数的作用如下:

  1. 第 2 行:从特定于电子表格中获取数据。我有不确定性,因为我阅读的许多文档和我找到的示例代码片段似乎使用了这一行的通用版本,它只要求“当前的电子表格”。这个想法是,这个函数被专门绑定到我打开脚本编辑器的表单上。为了安全起见,我已经输入了该表的确切 ID。我不希望这个函数意外地将数据写入我的 Google drive 中的另一个工作表。您可以在下面找到唯一的 ID:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Google 工作表的唯一 ID—按作者分类的图片

  1. 从 G 列开始,检查下一行的数据。这将创建一系列包含数据的行。
  2. 从第 2 列到第 7 列中抓取我们刚刚找到的范围长度的单元格块,以获得每一行的邮政编码
  3. 遍历每个单元格并创建一个纬度/经度数据的列表——是的,每次函数运行时都会遍历整个列表,这是一种浪费的编程。但是,我选择保留这一步,以防有人稍后对响应表单中的数据进行手动更改,并且数据不太可能超过 1000 行。
  4. 将此数据列表插入工作表的最后一列。这不包括检查最后一列数据的逻辑。它被明确地告知去 AB 和 AC 列。如果对 Google 表单进行了修改并添加了问题,您将需要手动更新该脚本。

Logger.log 是一个很好的工具,可以测试过程中的各个步骤,并查看每个变量是否都按照应有的方式存储。

功能触发器

仪表板构建的第 1 部分中的最后一步是配置将运行该脚本的触发器。谷歌让这变得超级简单。标准的选择是当文件被打开或数据被更改时运行脚本,但是这只能识别用户的更改,而不能识别由 Google 表单或其他脚本所做的更改。很简单,Google 还设置了一个基于表单提交的特定触发事件。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.从脚本编辑器中打开项目触发器—按作者排序的图像

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.为此脚本添加一个新的触发器—作者图片

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.用您需要的设置配置您的新触发器—按作者分类的图像

Code.gs

Code.gs 在这里是一个非常酷的工具。它有如此大的潜力,将极大地帮助我的实际工作。如果您的公司已经在使用 G-suite,那么使用您团队的现有流程,并添加一些自定义功能,作为 mvp 步骤来帮助他们。许多销售团队将为单个客户或项目存储他们的信息大型电子表格,只需要一点数据帮助。使用地图功能、电子邮件或日历连接工作表。

将 Python 连接到 Google Docs API

接下来,我们将把我们的应用程序直接连接到谷歌表单中的实时数据。

最初的搜索把我带到了 Google Docs Quickstar 页面。虽然它很全面,对于你可能在命令行中运行的应用程序来说也很棒,但对于我们在 Heroku 上托管这个应用程序的最终目标来说,它并不起作用。

更好的解决方案是使用谷歌服务账户

谢天谢地,另一个来自的帖子完美地解释了我们在这里需要做什么。

[## Python 与 Google Sheets 服务帐户:一步一步

几天前,我第一次体验了 Google Sheets API。我原以为这是一个简单的任务,但是,嗯…不是。

medium.com](https://medium.com/@denisluiz/python-with-google-sheets-service-account-step-by-step-8f74c26ed28e)

我不可能比上面的链接写得更好。相反,我会强调我在这个过程中遇到的地方,并指出我们将为我们的应用程序做出的一些轻微的偏差。

  1. 添加你的电子邮件——也许我读教程读得太快了,或者我认为因为我的个人电子邮件负责 API 和 Google 表单,权限已经设置好了。您需要将特殊的 API 电子邮件添加到 Google 工作表的“共享”部分,就像您邀请同事进行编辑一样。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

服务帐户设置屏幕截图-按作者分类的图像

2.安装 Google 客户端库——像许多数据科学项目一样,我使用 Anaconda 作为包管理器。我总是试着用它来管理依赖项,并在安装了其他包之后,把安装 pip 作为最后的选择。令人欣慰的是,Anaconda conda install -c conda-forge google-api-python-client google-auth-httplib2 google-auth-oauthlib中也包含了连接到 Google API 所需的包。最终,当我们到达将应用程序加载到 Heroku 的最后一步时,这些包将与 pip 一起下载,但在我测试时,我喜欢使用 Anaconda。

3.stringy 凭据— 如果我们要将该应用程序与 Heroku 一起使用,这与上面的中级教程有很大的不同。代替应用程序在当前工作目录中查找 credentials.json 文件,我们将使用。json 文件作为一个大字符串,并在我们的函数中将其转换回 json。

这是为了防止我们传递凭证文件并可能暴露它。Heroku 有一个内置的系统来传递这样的环境变量到我们的应用程序,就像我们的本地系统一样。

现在,为了在本地测试我们的项目,我们需要将它们添加到中。bashrc 或**。zshrc** 文件。如果你以前没有这样做过:来自김영석的精彩教程

  • 在文本编辑器中打开凭证文件,高亮显示并复制所有的凭证文件(包括{ & })。
  • 打开你的。bashrc 文件并添加新的环境变量,用单引号' '将您要粘贴的信息括起来:
  • export GDRIVE_AUTH='<paste your credentials here>'

4.找到要连接的数据的工作表 ID。重复上述步骤,为它创建一个环境变量。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Google 表单的唯一 ID——按作者分类的图片

  • export SPREADSHEET_ID='<paste your unique ID here>'

5.最后,在工作表中添加需要数据的特定区域作为环境变量

  • export RANGE_NAME='Form Responses 1!A1:AF'

去拿数据

我们的 Python 代码中的第一个任务是调用 Google Sheet API,并用最新的值创建一个 Pandas DataFrame。

Pandas 将允许我们在创建图表和视觉效果之前对数据进行大量的操作,并且会以足够快的速度完成这个网页。

这个函数需要我们刚刚提到的三个全局变量来定义 Google Sheet API 应该寻找什么。在最终的 Dash 应用程序脚本中,我们将在上面的函数之前定义这些。

  • SPREADSHEET_ID = <the unique code from the URL of the Sheet>
  • RANGE_NAME = <The name of the tab and range to look for>
  • CREDS = The stringified version of our Google API Credentials above

接下来,我们添加一个简单的函数,将电子表格中的值转换成 Pandas 数据帧。

现在,我们准备用结果数据创建一个 Plotly Dash 应用程序。

结论

这里,我们已经完成了构建仪表板的第一部分。我们现在有了一个从我们的数据存储中获取最新数据的流程,然后我们将构建一些可视化并将应用程序部署到 Heroku。

第二部分:

构建一个 Plotly Dash 应用程序

[## 从 Google Sheets 构建一个精巧的 Dash 应用程序——第 3 部分

到目前为止,我们已经创建了一个免费的用户友好的数据存储,并通过 Python API 连接到它

medium.com](https://medium.com/@derrickjameslewis/building-a-plotly-dash-app-from-google-sheets-part-3-666c496c8a71)

从 Google Sheets 构建一个精巧的 Dash 应用程序——第 2 部分

原文:https://towardsdatascience.com/building-a-plotly-dash-app-from-google-sheets-part-3-666c496c8a71?source=collection_archive---------27-----------------------

Plotly Dash 应用程序

创建一个 web 应用程序来显示我们新数据存储中的一些可视化效果。

到目前为止,我们已经创建了一个免费的、用户友好的数据存储,并通过 Google Python API 连接到它。

第 1 部分— 使用 Google 和 code.gs 进行设计和 ETL 过程

接下来,我们将使用这些数据为现场营销团队构建仪表板,以跟踪他们的活动和奖金进度。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

荒野中的自行车营销——作者图片

构建 Plotly Dash 应用程序

Dash 是 Plotly 的一个开源工具,它让数据科学家和其他像我一样对前端无知的人有机会组装一个交互式 web 应用程序,用几行 python 代码展示他们的 Plotly 图表。

Plotly Dash 上的文档非常棒,并且有很好的例子,直入主题。

仪表板基础

打开一个终端,为我们的项目创建一个新的环境。

conda create -n <yourEnvironmentName>

conda activate <yourEnvironmentName>

conda install plotly dash pandas numpy flask

从第 1 部分开始,我们将需要 Google API 库:

conda install -c conda-forge google-api-python-client google-auth-httplib2 google-auth-oauthlib

开始一个新的项目文件夹,并创建一个名为app.py的文件。我们已经准备好构建我们的 Dash 应用程序了。

  1. *【第 5 行】*启动烧瓶
  2. [第 7–8 行 ]使用刚刚创建的服务器创建一个 Dash 对象
  3. [第 10–20 行] 使用布局方法将组件和样式添加到该 Dash 对象。
  4. *【第 23 行】*运行服务器

就是这样。有了这 4 个核心部分,你现在就有了一个可以工作的应用程序。

Dash 的核心是应用程序的所有可视化组件都是 Python 类。对于您可能使用的组件类型,有特定的库可以导入,例如用于 div 和 image 等基本 HTML 项目的 HTML 库,用户友好的 Dash Core Components 库,其中包含预构建的下拉列表、列表、按钮、*图形(!)*等许多典型的 web 组件,最后是 Dash 数据表库来显示和操作页面上的大型数据表。

从终端的工作目录运行python app.py

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如果没有错误,您应该会看到上面的内容。这意味着您的电脑正在运行该应用程序。打开浏览器,进入 http://127.0.0.1:8050/ (或者上面给你的任何回复)。您将看到应用程序正在运行,并从您的布局中显示<h2>消息。还要注意浏览器右下角的蓝色圆圈。这对于将来调试和跟踪回调非常方便。

复试

理解我们的应用程序如何运行的另一个非常重要的步骤是回调。在我们启动 Flask 之后,创建一个 Dash 对象,并设计我们的应用程序可以响应更改的布局,例如在下拉列表中选择一个值,单击一个按钮或对一个表格进行排序。

Dash 中回调的基本结构如下所示:

@ app.callback(
    Output(component_id='graph', component_property='figure'),
    [Input(component_id='dropdown', component_property='value')]
)
def build_graph(selection): The first arguement in our function is the first item in the
    list of Inputs above. Name it as you like Do something here with the input.
    In this case it is the values from the dropdown. return the property needed for the output 
            (in this case it is a plotly figure object)

Dash 现在将监听我们名为“dropdown”的组件中的任何变化。当事情发生变化时,它将运行连接到回调的函数。然后将函数的输出返回给列出的组件。因此,用我们的选择更新页面。

快进

Plotly 为其 Dash 应用程序和可视化库提供了令人难以置信的文档。因此,我会让您找到您想要使用的特定工具,然后继续讨论我们将如何连接我们的 Google Sheet 以在仪表板中使用。

我们的建筑

首先,我们需要添加两个函数来从第 1 部分的中获取 Google 数据到我们的app.py脚本中。这将从我们的谷歌表获取数据,并将其转换成熊猫数据框架。

布局

接下来,我们将构建 Dash 应用程序的布局。Dash 的想法是,它是为那些对前端开发、React 或 Javacript 知之甚少的人提供的工具。对于像我一样的初学者,我将在下面强调两点。

  1. 仪表板自举组件

Bootstrap 是一个流行的 CSS 框架,用于在手机和其他屏幕上浏览响应站点。

在开发我的前几个 Dash 应用时,我努力调整布局。简单的任务,比如将一段文字居中或者增加页边空白是很困难的,很少能得到我想要的结果。

Dash Bootstrap Components 是第三方库,用于将 bootstrap CSS 集成到 Dash 中。文档非常棒,我从他们的视觉效果中学到了很多布局的工作原理。看一看:

https://dash-bootstrap-components . open source . faculty . ai

该库将容器、行和列的 Python 类添加到页面布局中。将这种结构与您的组件一起使用将允许站点更好地响应其他屏幕。对我来说,Python 类使得调整大小和对齐变得简单

在您的环境中安装 Dash 引导组件:

conda install -c condo-forge dash-bootstrap-components

2。数据表

数据表是 Dash 的一个组件,用于查看、编辑和浏览大型数据集。表格允许我们的应用程序用户过滤、排序和选择可以直接输入到我们的可视化数据。

他们非常强大,但也很挑剔。构建表格、列和标题非常简单,但是理解每个样式设置如何与其他样式设置交互可能需要一些时间来理解并导致意外的布局。

我遇到了与下面的单元格匹配的固定行标题的问题,以及某些 CSS 设置切断了数据的最左边和最右边的像素。

第一个提示是理解所有样式设置的优先级顺序:

  1. style data 有条件
  2. 样式 _ 数据
  3. style filter 有条件
  4. 样式 _ 过滤器
  5. style header 条件
  6. 样式 _ 标题
  7. 样式*_ 单元格 _* 条件
  8. 样式 _ 单元格

这意味着将类似minWidth=60的设置调整为“样式 _ 数据”,将覆盖“样式 _ 单元格”中maxWidth=50的任何重叠设置。

我在使用 Dash 数据表时的下一个技巧是为所有的样式设置建立一个单独的文件。在我在网上找到的许多例子和教程中,每一列数据都是使用 for 循环添加的,甚至一些条件格式的列和数据也是如此。虽然这不是最有效的,但使用单独的文件并手动构建每一列使我能够快速轻松地调试、测试和调整数据。因此,在学习用表格构建 Dash 应用程序时,明确一点是个好主意。您可以测试并学习如何更改数据格式、颜色和列宽。下面是我在这个仪表板上使用的例子:

请注意,在第三个列表“bonus_data_cond”中,我可以设置一个条件样式来检查我们的员工是否超过了他们的奖金目标。然后脚本相应地设置背景颜色。

我在我们的主脚本app.py中导入所有这些变量,并在上面的表格布局中分配它们,如下所示:style_cell_conditional=bonus_cell_cond

复试

接下来,我们将为每个可视化和交互式组件添加回调。我们要建造:

  1. 响应摘要统计
  2. 交互式地图
  3. 一个数据表,带有对应于数据选择的条形图。

特殊的数据回调

我们将从一些我在许多教程中没有发现的有趣的东西开始。在测试应用程序时,加载页面时数据通常不会刷新。此外,我发现一些资料提到了创建回调的最佳实践,该回调将数据作为字符串存储到html.Div()中,而不是让 python 脚本将数据作为每个函数都可以使用的全局变量存储在 Pandas DataFrame 中。对于这个数据存储库html.Div(),我们将设置style={'display':'none'}

但是为什么呢?

这为我们做了几件事。首先,它强制页面在每次加载时更新数据。其次,每个可视组件都将使用存储在这个隐藏组件中的数据来构建。API 调用和一些计算量更大的数据转换将只发生一次。对可视化的改变发生得更快,因为数据已经是干净的,只需要被过滤。最后,我知道如果数据不打算被看到或读取,这种做法有一点安全隐患。

因此,在我们的第一个页面加载期间,这个函数将由隐藏的html.Div()运行*。主数据转换将在这里发生,日期被转换,数字被存储为整数。然后 2 个响应回调将使用这些干净的数据进行过滤。所有数据都以字符串格式存储在.Div()组件中,而不是存储在服务器的内存中。幸运的是,这个项目永远无法处理非常大的数据集。如果这是您的情况,您应该找到一个替代方案来保持较低的内存使用率。*

实际上,我将设置两个数据收集回调。原因是允许仪表板在主地图和员工奖金表之间独立设置时间范围。

地图构建回调

在这里,我们深入了解 Plotly 的一些核心可视化优势。我们从我们的隐藏数据组件输入数据,并将其从字符串转换回 Pandas DF 进行一些操作,然后我们返回一个散点图以在上面的应用程序布局中呈现。

数据表回调

设置 Dash 数据表的大部分繁琐工作都是在布局和样式中完成的,这里我们使用 Pandas 对每个现场营销员工进行一些聚合,并返回一个字符串格式的数据供 dash_table 读取。布局中的 DataTable() 组件。

条形图回调

这是许多仪表板项目中我最喜欢的部分之一。数据表提供了选择行、突出显示、删除和过滤的灵活性。所有这些对表格的调整都可以作为结果可视化的输入发送(或者更好…机器学习预测!)

为什么这是我最喜欢的部分?因为我喜欢给仪表盘尽可能多的灵活性。用户可能想知道一个非常具体的问题的答案,“四月份在东部地区销售了多少个小部件?”一个好的仪表板设计给用户带来了灵活性,仪表板数据表本身就有这种功能。

这里,我们将从上表中的选定行中获取输入,并构建 4 个条形图。

这里需要强调的是,回调不仅可以接受多个输入,还可以将输出设置为一个列表并发送给多个组件。

汇总统计回调

这个回调简单明了地报告了给定初始日期选择器组件中设置的时间范围的清晰指标。大多数商业用户希望快速认知。这个报告的关键数字,有很大的空间。

把它们放在一起

以上所有部分组合成一个 Dash 应用程序。

下面的 Github 库包含了上面的核心部分,以及一些额外的地图,附加的过滤器和一些图片。

[## lewi 0332/谷歌仪表板

Plotly Dash 应用程序连接到 Google Sheets API。为 lewi0332/google_dashboard 开发做出贡献,创建一个…

github.com](https://github.com/lewi0332/google_dashboard)

结论

这是一个很大的步骤,需要在设计中进行大量繁琐的尝试和错误,但这是你可以让你的项目脱颖而出并为用户增加很多价值的地方。

第 3 部分—部署到 Heroku

[即将推出]

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们潜在仪表板的屏幕截图—作者提供的图片

构建预测模型来分类垃圾文本

原文:https://towardsdatascience.com/building-a-prediction-model-to-classify-texts-that-are-spam-a2ccded75e77?source=collection_archive---------32-----------------------

在这篇文章中,我和我的同事使用了一个文本信息数据集来建立一个预测模型,以分类哪些文本是垃圾邮件。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

照片由杰米街Unsplash

数据集

该数据集包含 5,574 封被标记为垃圾邮件或非垃圾邮件的邮件。该数据集被视为黄金标准,因为合法文本是为新加坡国立大学计算机科学系的研究而收集的,而垃圾短信是从英国的一个论坛中提取的,手机用户在该论坛上公开声称收到垃圾短信。

步骤

应对挑战的步骤包括:数据探索、数据预处理(标记化、词干化、词条化、空白、停用词等。),通过词云识别排名靠前的垃圾词,建立训练集和测试集,在训练集上建立分类模型,测试模型,最后对模型进行评估。

初始发行

最初面临的一个问题是,大部分数据集包含更高比例的合法文本消息。在将欺诈/垃圾邮件检测建模为分类问题时,一个常见的挑战是,在现实世界的数据中,大多数都不是欺诈性的,留给我们的是不平衡的数据。我们必须确保我们的训练数据集不会偏向合法的消息。处理不平衡数据有多种方法,如 SMOTE、RandomUnderSampler、ENN 等。我和我的团队引入了分层抽样。我们希望避免这种情况,即我们的模型预测大多数消息是合法的,而团队接受模型是合适的,因为尽管有偏差,但准确性很高。

流程

开发垃圾短信分类解决方案的方法包括:

初步文本分析

  • 使用饼图检查有多少邮件是垃圾邮件或合法邮件
  • 创建一个由垃圾邮件和非垃圾邮件组成的单词云
  • 识别前 10 个垃圾邮件单词和前 10 个合法单词
  • 分析垃圾邮件和合法文本消息的长度,并分别绘制两个图表来检查它们的长度分布。

文本转换

  • 通过删除停用词、执行标记化、词干化、词条化、空白等来清理数据。
  • 我们使用 NLTK 库中的‘SnowballStemmer’从单词中移除形态词缀,只留下单词词干
  • 我们使用“TfidfVectorizer”从提供的计数矩阵执行 TF-IDF 转换
  • 我们在数据集中对类别进行了编码:“垃圾邮件”为 1,“合法”为 0
  • 我们以 80:20 的比例分割训练和测试数据

分析

我们通过数据可视化和探索注意到的几个快速观察结果是,大约 86.6%的数据是合法的,而其余 13.4%是垃圾数据。我们还捕获了最常见的垃圾邮件单词(“呼叫”、“免费”、“Txt”、“发送”、“停止”、“回复”)和合法单词(“u”、“gt”、“lt”、“ok”、“get”、“go”)。发现的其他见解是,与合法消息相比,垃圾消息的长度往往更长。我们绘制了以下图表进行探索性分析:

被归类为垃圾邮件或火腿的文本百分比(合法)

#Lets see what precentage of our data is spam/ham texts["category"].value_counts().plot(kind = 'pie', explode = [0, 0.2], figsize = (6, 6), a plt.ylabel("Spam vs Ham") plt.legend(["Ham", "Spam"]) plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

来自数据集的前 10 条短信

#top ham/spam messages message toptexts = texts.groupby("text")["category"].agg([len, np.max]).sort_values(by = "len", asc display(toptexts)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

垃圾词云

#Spam Word cloud spam_wordcloud = WordCloud(width=600, height=400).generate(" ".join(spam_words)) plt.figure( figsize=(10,8), facecolor='k') plt.imshow(spam_wordcloud) plt.axis("off") plt.tight_layout(pad=0) plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

火腿(合法)字云

#Ham word cloud ham_wordcloud = WordCloud(width=600, height=400).generate(" ".join(ham_words)) plt.figure( figsize=(10,8), facecolor='k') plt.imshow(ham_wordcloud) plt.axis("off") plt.tight_layout(pad=0) plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

火腿和垃圾短信长度分布

f, ax = plt.subplots(1, 2, figsize = (20, 6)) sns.distplot(texts[texts["category"] == "spam"]["messageLength"], bins = 20, ax = ax[0]) ax[0].set_xlabel("Spam Message Word Length") sns.distplot(texts[texts["category"] == "ham"]["messageLength"], bins = 20, ax = ax[1]) ax[0].set_xlabel("Ham Message Word Length") plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结果

模型应用

我们使用朴素贝叶斯来训练我们的模型。我们选择这种模型是因为在处理文本时,将每个独特的单词视为一个特征是非常常见的,并且由于典型的人的词汇量是成千上万的单词,这导致了大量的特征。当单词的多次出现在分类问题中很重要时,该算法是相关的。该算法的相对简单性和朴素贝叶斯的独立特征假设使其成为分类文本的强有力的执行者,因此我们决定使用朴素贝叶斯分类器来将文本消息分类为合法的或垃圾邮件。

模型评估

我们认为精确度是更好的评估标准,因为更有必要不将合法邮件归类为垃圾邮件。因此,我们致力于创建一个具有更好的可接受的 f-beta 分数的模型,并倾向于将精确度作为我们提出的问题的解决方案的准确性度量。结果,我们发现该模型的 f-beta 分数为 0.93,这可以被认为是足够好的,可以推断所建立的模型非常接近预期使用的理想模型。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

娜塔莉·加尔塞斯 是一名来自大西雅图地区的数据分析师。她获得了华盛顿大学的商业和 MSBA 学士学位。她写作并热衷于数据可视化、投资和所有科技方面的东西。

在不发布的情况下构建 Python 包

原文:https://towardsdatascience.com/building-a-python-package-without-publishing-e2d36c4686cd?source=collection_archive---------13-----------------------

轻松访问和组织您的 Python 模块

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

UnsplashLeone Venter 拍摄的照片

python 中的包允许无缝分发 Python 模块。当我们使用 pip install 时,我们经常从 PyPI 下载一个公开可用的包。本地包对于代码组织和重用也很有用,允许您简单地导入一个模块,而不必导航到它的目录或重写代码。在本次演示中,我们将构建和访问一个基本的 Python 包。

让我们创建两个简单的模块并将它们打包。然后,我们将使用这个包编写一个简单的应用程序,提示用户从他们的计算机中选择一个. jpg 图像进行灰度化。

编写您的模块

ui.py

#ui.pyfrom tkinter import filedialog
from tkinter import Tkdef get_image_file():
    Tk().withdraw()
    filename =  filedialog.askopenfilename(title = "Select file",filetypes = [("jpeg files","*.jpg")])
    return filename

image_edits.py

#image_edits.pyfrom cv2 import imread, COLOR_RGB2GRAY, cvtColor, imwrite
import osdef write_grayscale_image(filepath):
    original_image = imread(filepath)
    grayed_image = cvtColor(original_image, COLOR_RGB2GRAY)
    grayed_filename=os.path.join(os.path.split(filepath)[0],'grayed_'+os.path.split(filepath)[1])
    print(grayed_filename)
    imwrite(grayed_filename, grayed_image) #export grayscaled image
    return grayed_filename

初始化和设置文件

创建一个空白的 init。py 文件,Python 将使用它来识别这个包。

$ touch __init__.py

此时,我们的文件结构应该如下所示:

dir/
    image_pkg/
              ui.py
              image_edits.py
              __init__.py

接下来,在您的包目录外创建一个 setup.py 文件

setup.py

import setuptoolssetuptools.setup(name='examplepackage',
version='0.1',
description='An example package',
url='#',
author='max',
install_requires=['opencv-python'],
author_email='',
packages=setuptools.find_packages(),
zip_safe=False)

注意,python 标准库中没有包含的包应该包含在 install_requires 中。我们的文件结构现在应该看起来像这样:

dir/
    image_pkg/
              ui.py
              image_edits.py
              __init__.py
    setup.py

构建并安装您的软件包

如果您正在使用虚拟环境(Python 开发的一般良好实践),创建并激活您的环境。

$ python3 -m venv myenv
$ source myenv/bin/activate

安装车轮。

$ pip install wheel

将软件包安装到您的环境中。

$ pip install .

我们的包已经创建好了,现在我们可以在任何地方使用它。让我们创建一个简单的应用程序,它包含了我们的包内容。

main.py

#main.pyfrom image_pkg.ui import get_image_file
from image_pkg.image_edits import write_grayscale_imagewrite_grayscale_image(get_image_file())

我们现在已经构建并实现了一个基本的 python 包!关于软件包分发、许可和安装的更多信息可以在文档中找到。

其他 Python 教程:

[## 用 Python 解迷宫

使用 Dijkstra 的算法和 OpenCV

towardsdatascience.com](/solving-mazes-with-python-f7a412f2493f) [## 为 Python 构建一个简单的 UI

Streamlit:一个基于浏览器的 Python UI,不需要 HTML/CSS/JS

towardsdatascience.co](/building-a-simple-ui-for-python-fd0e5f2a2d8b)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值