在此模块中,您将使用内置的 Amazon SageMaker k-最近邻 (k-NN) 算法来训练内容推荐模型。

Amazon SageMaker K-最近邻 (k-NN) 是基于索引的非参数型监督学习算法,可用于分类和回归任务。对于分类,该算法会查询最接近目标的 k 点,并返回类中最常用的标签作为预测标签。对于回归问题,该算法将返回 k 最近邻返回的预测值的平均值。

使用 k-NN 算法训练分为三步:抽样、维度缩减和索引构建。抽样可缩减初始数据集的大小,从而使其适应内存。对于维度缩减,该算法通过减少数据的特征维度来降低 k-NN 模型在内存和推理延迟中的占用量。我们提供两种维度缩减方法:随机投影和快速的 Johnson-Lindenstrauss 转换。通常情况下,您可以将维度缩减用于高维 (d >1000) 数据集,以免“维度灾难”对数据的统计分析造成困扰,因为随着维度增加,数据会变得稀疏。k-NN 训练的主要目标是构建索引。索引可用于高效查询尚未确定值或类标签的点之间的距离,以及用于推理的 k 邻近点。

在下面的步骤中,您将为训练作业指定 k-NN 算法,设置超参数值来调整模型及运行模型。然后,您可以将模型部署到 Amazon SageMaker 管理的终端节点中,以进行预测。

完成模块所需时间:20 分钟


  • 步骤 1.创建并运行训练作业

    在前面的模块中,您创建了主题向量。在本模块中,您将构建并部署其中保留了主题向量索引的内容推荐模块。

    首先,创建一个字典将随机化标签与训练数据中的原始标签关联。在您的笔记本中,复制并粘贴以下代码,然后选择运行

    labels = newidx 
    labeldict = dict(zip(newidx,idx))

    接下来,使用以下代码将训练数据存储在您的 S3 存储桶中:

    import io
    import sagemaker.amazon.common as smac
    
    
    print('train_features shape = ', predictions.shape)
    print('train_labels shape = ', labels.shape)
    buf = io.BytesIO()
    smac.write_numpy_to_dense_tensor(buf, predictions, labels)
    buf.seek(0)
    
    bucket = BUCKET
    prefix = PREFIX
    key = 'knn/train'
    fname = os.path.join(prefix, key)
    print(fname)
    boto3.resource('s3').Bucket(bucket).Object(fname).upload_fileobj(buf)
    s3_train_data = 's3://{}/{}/{}'.format(bucket, prefix, key)
    print('uploaded training data location: {}'.format(s3_train_data))
    

    接下来,使用下面的帮助程序函数创建 k-NN 估算器,很像您在模块 3 中创建的 NTM 估算器。

    def trained_estimator_from_hyperparams(s3_train_data, hyperparams, output_path, s3_test_data=None):
        """
        Create an Estimator from the given hyperparams, fit to training data, 
        and return a deployed predictor
        
        """
        # set up the estimator
        knn = sagemaker.estimator.Estimator(get_image_uri(boto3.Session().region_name, "knn"),
            get_execution_role(),
            train_instance_count=1,
            train_instance_type='ml.c4.xlarge',
            output_path=output_path,
            sagemaker_session=sagemaker.Session())
        knn.set_hyperparameters(**hyperparams)
        
        # train a model. fit_input contains the locations of the train and test data
        fit_input = {'train': s3_train_data}
        knn.fit(fit_input)
        return knn
    
    hyperparams = {
        'feature_dim': predictions.shape[1],
        'k': NUM_NEIGHBORS,
        'sample_size': predictions.shape[0],
        'predictor_type': 'classifier' ,
        'index_metric':'COSINE'
    }
    output_path = 's3://' + bucket + '/' + prefix + '/knn/output'
    knn_estimator = trained_estimator_from_hyperparams(s3_train_data, hyperparams, output_path)
    

    训练作业运行时,仔细看看帮助程序函数中的参数。

    Amazon SageMaker k-NN 算法提供很多不同的距离指标来计算最近的邻居。自然语言处理中常用的一个指标为 cosine 距离。在数学上,向量 A 与 B 之间的余弦“相似度”由以下等式给出:

    通过将 index_metric 设置为 COSINE,Amazon SageMaker 自动使用余弦相似度计算最近的邻居。默认距离为 L2 norm,它是标准的欧氏距离。请注意,在发布时,COSINE 仅支持 faiss.IVFFlat 索引类型,不支持 faiss.IVFPQ 索引方法。

    您应会在终端中看到以下输出。

    Completed - Training job completed

    成功! 由于您希望此模型在已知特定测试主题的情况下返回最近的邻居,您需要将其部署为实时托管终端节点。

  • 第 2 步:部署内容推荐模型

    正如您使用 NTM 模型所做的那样,为 k-NN 模型定义以下帮助程序函数来启动终端节点。在帮助程序函数中,接受令牌 applications/jsonlines; verbose=true 告诉 k-NN 模型返回所有的余弦距离,而不仅仅是最近的邻居。要构建推荐引擎,您需要通过模型获得 top-k 建议,为此,您需要将 verbose 参数设置为 true,而不是默认的 false。

    将以下代码复制并粘贴到您的笔记本中,然后选择运行

    def predictor_from_estimator(knn_estimator, estimator_name, instance_type, endpoint_name=None): 
        knn_predictor = knn_estimator.deploy(initial_instance_count=1, instance_type=instance_type,
                                            endpoint_name=endpoint_name,
                                            accept="application/jsonlines; verbose=true")
        knn_predictor.content_type = 'text/csv'
        knn_predictor.serializer = csv_serializer
        knn_predictor.deserializer = json_deserializer
        return knn_predictor
    import time
    
    instance_type = 'ml.m4.xlarge'
    model_name = 'knn_%s'% instance_type
    endpoint_name = 'knn-ml-m4-xlarge-%s'% (str(time.time()).replace('.','-'))
    print('setting up the endpoint..')
    knn_predictor = predictor_from_estimator(knn_estimator, model_name, instance_type, endpoint_name=endpoint_name)

    接下来,预处理测试数据,以便您可以运行推理。

    将以下代码复制并粘贴到您的笔记本中,然后选择运行

    def preprocess_input(text):
        text = strip_newsgroup_header(text)
        text = strip_newsgroup_quoting(text)
        text = strip_newsgroup_footer(text)
        return text    
        
    test_data_prep = []
    for i in range(len(newsgroups_test)):
        test_data_prep.append(preprocess_input(newsgroups_test[i]))
    test_vectors = vectorizer.fit_transform(test_data_prep)
    
    test_vectors = np.array(test_vectors.todense())
    test_topics = []
    for vec in test_vectors:
        test_result = ntm_predictor.predict(vec)
        test_topics.append(test_result['predictions'][0]['topic_weights'])
    
    topic_predictions = []
    for topic in test_topics:
        result = knn_predictor.predict(topic)
        cur_predictions = np.array([int(result['labels'][i]) for i in range(len(result['labels']))])
        topic_predictions.append(cur_predictions[::-1][:10])       
    

    在本模块的最后一个步骤中,探索您的内容推荐模型。

  • 第 3 步:探索内容推荐模型

    现在,您已获得预测,您可以将测试主题的主题分布绘制成图形,与 k-NN 模型推荐的最近 k 主题进行比较。

    将以下代码复制并粘贴到您的笔记本中,然后选择运行

    # set your own k.
    def plot_topic_distribution(topic_num, k = 5):
        
        closest_topics = [predictions[labeldict[x]] for x in topic_predictions[topic_num][:k]]
        closest_topics.append(np.array(test_topics[topic_num]))
        closest_topics = np.array(closest_topics)
        df = pd.DataFrame(closest_topics.T)
        df.rename(columns ={k:"Test Document Distribution"}, inplace=True)
        fs = 12
        df.plot(kind='bar', figsize=(16,4), fontsize=fs)
        plt.ylabel('Topic assignment', fontsize=fs+2)
        plt.xlabel('Topic ID', fontsize=fs+2)
        plt.show()
    

    运行以下代码以将主题分布绘制成图形:

    plot_topic_distribution(18)
    

    现在,尝试一些其他主题。运行以下代码单元格:

    plot_topic_distribution(25)
    plot_topic_distribution(5000)

    根据您选择的主题数量 (NUM_TOPICS),您的图可能会有点不同。但总体来说,这些图显示,k-NN 模型使用余弦相似度发现的最邻近文档的主题分布与我们输入到模型中的测试文档的主题分布非常相似。

    结果表明,k-NN 可能是构建基于语义的信息检索系统的一个很好的方式,其方法是首先将文档内嵌到主题向量中,然后使用 k-NN 模型提供推荐。


恭喜! 在此模型中,您训练、部署和探索了您的内容推荐模型。

在下一个模块中,您将清理您在此实验室中使用的资源。