整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

DashVector向量数据库 + ModelSco

DashVector向量数据库 + ModelScope 大模型玩转多模态检

文主要介绍通过DashVector和ModelScope中的Chinese Clip模型实现文搜图、图搜图等功能,同时结合DashText SDK实现sparse vector+dense vector混合检索,熟悉sparse vector的使用方法,提高检索效率。

1 准备工作


1.1 基本概念


  1. Chinese Clip:为CLIP模型的中文版本,使用大规模中文数据进行训练(~2亿图文对),可用于图文检索和图像、文本的表征提取,应用于搜索、推荐等应用场景。详情请参考:https://modelscope.cn/models/iic/multi-modal_clip-vit-huge-patch14_zh/summary
  2. DashVector:向量检索服务基于阿里云自研的向量引擎 Proxima 内核,提供具备水平拓展、全托管、云原生的高效向量检索服务。向量检索服务将强大的向量管理、查询等能力,通过简洁易用的 SDK/API 接口透出,方便在大模型知识库搭建、多模态 AI 搜索等多种应用场景上集成。详情请参考:https://www.aliyun.com/product/ai/dashvector
  3. MUGE数据集:MUGE(牧歌,Multimodal Understanding and Generation Evaluation)是业界首个大规模中文多模态评测基准,由达摩院联合浙江大学、阿里云天池平台联合发布,中国计算机学会计算机视觉专委会(CCF-CV专委)协助推出。目前包括: · 包含多模态理解与生成任务在内的多模态评测基准,其中包括图像描述、图文检索以及基于文本的图像生成。未来我们将公布更多任务及数据。 · 公开的评测榜单,帮助研究人员评估模型和追踪进展。 MUGE旨在推动多模态表示学习进展,尤其关注多模态预训练。具备多模态理解和生成能力的模型均可以参加此评测,欢迎各位与我们共同推动多模态领域发展。详情请参考:https://modelscope.cn/datasets/modelscope/muge/summary
  4. DashText:是向量检索服务DashVector推荐使用的稀疏向量编码器(Sparse Vector Encoder),DashText可通过BM25算法将原始文本转换为稀疏向量(Sparse Vector)表达,通过DashText可大幅度简化使用DashVector关键词感知检索能力。详情请参考:https://help.aliyun.com/document_detail/2546039.html

1.2 准备工作


  1. 获取DashVector的API-KEY。API-KEY用于访问DashVector服务,详请参考:https://help.aliyun.com/document_detail/2510230.html

  1. 申请DashVector测试实例,DashVector提供免费试用实例,可以薅一波。详情请见:https://help.aliyun.com/document_detail/2568083.html
  2. 获取DashVector实例的endpoint,endpoint用于访问DashVector具体的实例。详情请见:https://help.aliyun.com/document_detail/2568084.html

  1. 安装DashVector、DashText、ModelScope的SDK
pip install dashvector
pip install dashtext
pip install modelscope

由于安装ModelScope SDK需要一些依赖,继续安装,安装的时间有点长,请耐心等待~~~~~

pip install decord
pip install torch torchvision opencv-python timm librosa fairseq transformers unicodedata2 zhconv rapidfuzz

由于本教程中,会使用DashText的sdk生成稀疏向量,生成稀疏向量过程中会先下载一个词包,下载过程比较长。所以可以预先下载。

wget https://dashvector-data.oss-cn-beijing.aliyuncs.com/public/sparsevector/bm25_zh_default.json

好啦,SDK和依赖都安装完了,下面简单介绍一下多模态搜索的过程。

1.3 多模态搜索过程

  1. 多模态搜索分为两个过程,即索引过程和搜索过程。
  2. 索引过程:本教程在索引过程中,使用MUGE数据集,数据格式如下。只需要对MUGE数据集中的图片和文本提取特征,然后将特征插入到DashVector中,就完成了索引过程。
[{
    "query_id": "54372",
    "query": "金属产品打印",
    "image_id": "813904",
    "image": <PIL.PngImagePlugin.PngImageFile image mode=RGB size=224x224 at 0x7F8EB1F39DB0>
},
{
    "query_id": "78633",
    "query": "夹棉帽子",
    "image_id": "749842",
    "image": <PIL.PngImagePlugin.PngImageFile image mode=RGB size=224x224 at 0x7F8EB0AFFF70>
}]
  1. 搜索过程:通过对输入的文本或者图片,提取特征,并通过特征在DashVector中已经索引的向量中进行相似向量查询,并将查询后的结果解析成可视化的图片和文本,即完成了搜索过程。详情请看下图。

2 创建DashVector Collection

from dashvector import Client


# 如下填写您在1.2 准备工作中获取的DashVector API-KEY
DASHVECTOR_API_KEY='{YOUR DashVector API-KEY}'
# 如下填写您在1.2 准备工作中获取的DashVector中Cluster中的Endpoint
DASHVECTOR_END_POINT='{YOUR DashVector Endpoint}'

# 初始化DashVector 的client
client=Client(api_key=DASHVECTOR_API_KEY, endpoint=DASHVECTOR_END_POINT)

response=client.create(
    # Collection的名称,名称可自定义。这里暂时定义为:ImageTextSearch
    name='ImageTextSearch',
    # 创建Collection的维度,注意一定是1024维。因为后面我们会使用Chinese Clip模型进行embedding,Chinese Clip模型的输出维度是1024维。
    dimension=1024,
    # 距离度量方式一定为dotproduct,因为稀疏向量只支持dotproduc这种度量方式。
    metric='dotproduct',
    dtype=float,
    # 定义schema,通过schema可以定义Collection中包含哪些字段,以及字段的类型,以便实现更快速的搜索。这里定义了image_id、query和query_id三个schema。
  # 关于Schema的详细使用请参考:https://help.aliyun.com/document_detail/2510228.html
    fields_schema={'image_id': int, 'query': str, 'query_id': int}
)

print(response)

好啦,Collection创建成功了。

3 图片和文本索引

  1. 图片和文本插入,由于涉及到图片特征提取,所以速度会有点慢,建议使用GPU进行特征提取。
# 首先import一大堆东西
from modelscope.msdatasets import MsDataset
from modelscope.utils.constant import Tasks
from modelscope.pipelines import pipeline
import base64
import io
from dashvector import Client, Doc, DashVectorCode, DashVectorException
from dashtext import SparseVectorEncoder


# load 数据集,选取modelscope中的muge数据集,取数据集中validation部分
# muge数据集的格式为:
# [{
#     "query_id": "54372",
#     "query": "金属产品打印",
#     "image_id": "813904",
#     "image": <PIL.PngImagePlugin.PngImageFile image mode=RGB size=224x224 at 0x7F8EB1F39DB0>
# },
# {
#     "query_id": "78633",
#     "query": "夹棉帽子",
#     "image_id": "749842",
#     "image": <PIL.PngImagePlugin.PngImageFile image mode=RGB size=224x224 at 0x7F8EB0AFFF70>
# }]
# 首次load muge数据集有点慢,请耐心等待。
datasets=MsDataset.load("muge", split="validation")
# 获取数据集的长度,也就是数据集中有多少个这样的数据
datasets_len=len(datasets)


# 初始化ModelScope推理pipeline,本教程使用Chinese Clip作为embedding模型。由于图片的Embedding比较消耗计算资源,所以推荐使用GPU进行推理加速。
# 请参考:https://modelscope.cn/models/iic/multi-modal_clip-vit-huge-patch14_zh/summary
pipeline=pipeline(task=Tasks.multi_modal_embedding,
                    model='damo/multi-modal_clip-vit-huge-patch14_zh', model_revision='v1.0.0')



# 初始化稀疏向量编码器,用于对muge数据集中的query进行稀疏向量编码,中文编码。详情请参考:https://help.aliyun.com/document_detail/2546039.html
encoder=SparseVectorEncoder()
# encoder初始化的时间比较长,主要原因在于稀疏向量编码器需要加载一个json文件,该文件比较大,需要下载。我们可以先下载完,保存在本地,直接加载,速度会快很多。
# 下载链接:https://dashvector-data.oss-cn-beijing.aliyuncs.com/public/sparsevector/bm25_zh_default.json
# 也可以使用:wget https://dashvector-data.oss-cn-beijing.aliyuncs.com/public/sparsevector/bm25_zh_default.json,直接下载到本地。
# 下载完成后,放在本机目录中,本教程已经下载完成,放在根目录下。
encoder_path='bm25_zh_default.json'
encoder.load(encoder_path)



# 如下填写您在1.2 准备工作中获取的DashVector API-KEY
DASHVECTOR_API_KEY='{YOUR DashVector API-KEY}'
# 如下填写您在1.2 准备工作中获取的DashVector中Cluster中的Endpoint
DASHVECTOR_END_POINT='{YOUR DashVector Endpoint}'

# 初始化dashvector的Client,用于访问dashvector服务
# 请参考:https://help.aliyun.com/document_detail/2510240.html
client=Client(api_key=DASHVECTOR_API_KEY, endpoint=DASHVECTOR_END_POINT)




# 将图片转成字符串,用于将图片存储在dashvector中
def image_to_str(image):
    image_byte_arr=io.BytesIO()
    image.save(image_byte_arr, format='PNG')
    image_bytes=image_byte_arr.getvalue()
    return base64.b64encode(image_bytes).decode()


# 通过Chinese Clip提取图片特征,并转成向量
def image_vector(image):
    # 通过Chinese Clip提取图片特征,返回为一个tensor
    img_embedding=pipeline.forward({'img': image})['img_embedding']
    # 将返回的tensor转成向量,向量需要转存到cpu中
    img_vector=img_embedding.detach().cpu().numpy()
    return img_vector if isinstance(image, list) else img_vector[0]


# 通过Chinese Clip提取文本特征,并转成向量
def text_vector(text):
    # 通过Chinese Clip提取文本特征,返回为一个tensor
    text_embedding=pipeline.forward({'text': text})['text_embedding']
    # 将返回的tensor转成向量,向量需要转存到cpu中
    text_vector=text_embedding.detach().cpu().numpy()
    return text_vector if isinstance(text, list) else text_vector[0]


# 通过dashtext对文本生成稀疏向量。注意,本函数为生成入库的稀疏向量,而不是query的稀疏向量
def sparse_vector_documents(text):
    # 通过dashtext生成稀疏向量
    sparse_vector=encoder.encode_documents(text)
    return sparse_vector if isinstance(text, list) else sparse_vector


# 插入向量数据,batch_size默认为10,最大不超过20
def insert_docs(collection_name: str, partition='default', batch_size=10):
    idx=0
    while idx < datasets_len:
        # 获取batch range数据
        batch_range=range(idx, idx + batch_size) if idx + batch_size < datasets_len else range(idx, datasets_len)

        # 获取image信息
        images=[datasets[i]['image'] for i in batch_range]
        # 通过Chinese Clip提取图片特征,返回为一个vector
        images_vector=image_vector(images)

        # 获取query信息
        texts=[datasets[i]['query'] for i in batch_range]
        # 生成稀疏向量
        documents_sparse_vector=sparse_vector_documents(texts)

        # 获取图片ID和query ID
        images_ids=[datasets[i]['image_id'] for i in batch_range]
        query_ids=[datasets[i]['query_id'] for i in batch_range]

        # 获取Collection
        collection=client.get(collection_name)
        # 批量插入
        response=collection.upsert(
            [
                Doc(
                    id=image_id,
                    vector=img_vector,
                    sparse_vector=document_sparse_vector,
                    fields={
                        # 由于在创建Collection时,image_id和query_id都是int类型,所以这里需要转换为int类型
                        'image_id': int(image_id),
                        'query_id': int(query_id),
                        'query': query,
                        # 将Image格式转成字符串,用于存储在dashvector中
                        'image': image_to_str(image)
                    }
                ) for img_vector, document_sparse_vector, image_id, query_id, image, query in
                zip(images_vector, documents_sparse_vector, images_ids, query_ids, images, texts)
            ]
        )
        print(response)
        idx +=batch_size

    return response



if __name__=='__main__':
    # 插入数据
    response=insert_docs(collection_name='ImageTextSearch', batch_size=20)
  1. 向量插入后,就可以在DashVector控制台看到向量啦!https://dashvector.console.aliyun.com/cn-hangzhou/cluster/

4 图片和文本搜索

  1. 图片插入成功后,即可进行图片和文本的跨模态搜索了,同样由于搜索过程中,涉及到图片特征提取,建议使用GPU进行。
# 老规矩,先import一堆东西
from modelscope.utils.constant import Tasks
from modelscope.preprocessors.image import load_image
from modelscope.pipelines import pipeline
from PIL import Image
import base64
import io
from dashvector import Client, Doc, DashVectorCode, DashVectorException
from dashtext import SparseVectorEncoder, combine_dense_and_sparse
from urllib.parse import urlparse



# 初始化ModelScope推理pipeline,本教程使用Chinese Clip作为embedding模型。由于图片的Embedding比较消耗计算资源,所以推荐使用GPU进行推理加速。
# 请参考:https://modelscope.cn/models/iic/multi-modal_clip-vit-huge-patch14_zh/summary
pipeline=pipeline(task=Tasks.multi_modal_embedding,
                    model='damo/multi-modal_clip-vit-huge-patch14_zh', model_revision='v1.0.0')



# 初始化稀疏向量编码器,用于对muge数据集中的query进行稀疏向量编码,中文编码。详情请参考:https://help.aliyun.com/document_detail/2546039.html
encoder=SparseVectorEncoder()
# encoder初始化的时间比较长,主要原因在于稀疏向量编码器需要加载一个json文件,该文件比较大,需要下载。我们可以先下载完,保存在本地,直接加载,速度会快很多。
# 下载链接:https://dashvector-data.oss-cn-beijing.aliyuncs.com/public/sparsevector/bm25_zh_default.json
# 也可以使用:wget https://dashvector-data.oss-cn-beijing.aliyuncs.com/public/sparsevector/bm25_zh_default.json,直接下载到本地。
# 下载完成后,放在本机目录中,本教程已经下载完成,放在根目录下。
encoder_path='bm25_zh_default.json'
encoder.load(encoder_path)



# 如下填写您在1.2 准备工作中获取的DashVector API-KEY
DASHVECTOR_API_KEY='{YOUR DashVector API-KEY}'
# 如下填写您在1.2 准备工作中获取的DashVector中Cluster中的Endpoint
DASHVECTOR_END_POINT='{YOUR DashVector Endpoint}'

# 初始化dashvector的Client,用于访问dashvector服务
# 请参考:https://help.aliyun.com/document_detail/2510240.html
client=Client(api_key=DASHVECTOR_API_KEY, endpoint=DASHVECTOR_END_POINT)


# 将字符串转为图片
def str2image(image_str):
    image_bytes=base64.b64decode(image_str)
    return Image.open(io.BytesIO(image_bytes))


# 判断是否为URL
def is_url(url):
    try:
        result=urlparse(url)
        return all([result.scheme, result.netloc])
    except ValueError:
        return False


# 通过Chinese Clip提取图片特征,并转成向量
def image_vector(image):
    # 通过Chinese Clip提取图片特征,返回为一个tensor
    img_embedding=pipeline.forward({'img': image})['img_embedding']
    # 将返回的tensor转成向量,向量需要转存到cpu中
    img_vector=img_embedding.detach().cpu().numpy()
    return img_vector if isinstance(image, list) else img_vector[0]


# 通过Chinese Clip提取文本特征,并转成向量
def text_vector(text):
    # 通过Chinese Clip提取文本特征,返回为一个tensor
    text_embedding=pipeline.forward({'text': text})['text_embedding']
    # 将返回的tensor转成向量,向量需要转存到cpu中
    text_vector=text_embedding.detach().cpu().numpy()
    return text_vector if isinstance(text, list) else text_vector[0]


# 通过dashtext对文本生成稀疏向量。注意,本函数为query的稀疏向量,而不是入库的稀疏向量
def sparse_vector_queries(text):
    # 通过dashtext生成稀疏向量
    sparse_vector=encoder.encode_queries(text)
    return sparse_vector if isinstance(text, list) else sparse_vector


# 通过文本和图片搜索图片,返回搜索结果。其中,文本会转换为稀疏向量,图片会转换成稠密向量,并通过alpha值控制稠密向量和稀疏向量的权重,alpha=1.0则全部使用稠密向量搜索,alpha=0.0则全部使用稀疏向量搜索
def serach_by_imageAndtext(query_text, query_image, collection_name, partition='default', top_k=10, alpha=0.5):
    if is_url(query_image):
        query_image=load_image(query_image)
    image_embedding=image_vector(query_image)
    query_sparse_embedding=sparse_vector_queries(query_text)
    scaled_dense_vector, scaled_sparse_vector=combine_dense_and_sparse(image_embedding, query_sparse_embedding, alpha)
    try:
        collection=client.get(name=collection_name)
        # 搜索
        docs=collection.query(
            vector=scaled_dense_vector,
            sparse_vector=scaled_sparse_vector,
            partition=partition,
            topk=top_k,
            output_fields=['image', 'query', 'image_id']
        )

        image_list=list()
        for doc in docs:
            image_str=doc.fields['image']
            # print(doc.score)
            # print(doc.fields['query'])
            # print(doc.fields['image_id'])
            image_list.append(str2image(image_str))
        return image_list

    except DashVectorException as e:
        print(e)
        return []


# 通过文本搜索图片,返回搜索结果,并将文本变成对应的稀疏向量和稠密向量,稀疏向量用来控制文本中是否包含该关键词,稠密向量用于控制图片中是否包含此信息。可通过alpha值控制稠密向量和稀疏向量的权重,alpha=1.0则全部使用稠密向量搜索,alpha=0.0则全部使用稀疏向量搜索
def search_by_text(query_text, collection_name, partition='default', top_k=10, alpha=0.5):
    query_embedding=text_vector(query_text)
    print(query_embedding)
    print(type(query_embedding))
    print(query_embedding.dtype)
    query_sparse_embedding=sparse_vector_queries(query_text)
    scaled_dense_vector, scaled_sparse_vector=combine_dense_and_sparse(query_embedding, query_sparse_embedding, alpha)
    try:
        collection=client.get(name=collection_name)
        # 搜索
        docs=collection.query(
            vector=scaled_dense_vector,
            sparse_vector=scaled_sparse_vector,
            partition=partition,
            topk=top_k,
            output_fields=['image', 'query', 'image_id']
        )

        image_list=list()
        for doc in docs:
            image_str=doc.fields['image']
            # print(doc.score)
            # print(doc.fields['query'])
            # print(doc.fields['image_id'])
            image_list.append(str2image(image_str))
        return image_list

    except DashVectorException as e:
        print(e)
        return []


if __name__=='__main__':
    query_text='女士帽子'
    query_image='https://cbu01.alicdn.com/img/ibank/O1CN01XjQLIb2JjMX6sVhn7_!!2217497569457-0-cib.jpg?__r__=1711033209457'
    # response=search_by_text(query_text=query_text, collection_name='ImageTextSearch', alpha=1.0)
    response=serach_by_imageAndtext(query_text=query_text, query_image=query_image, collection_name='ImageTextSearch',
                                      top_k=20, alpha=0.8)
    for image in response:
        image.show()
  1. 搜索结果出来啦!

译:张逸

校对:冯羽

本文约2804字,建议阅读7分钟。

本文将教你如何使用Keras这个Python库完成深度学习模型的分类与回归预测。

当你在Keras中选择好最合适的深度学习模型,就可以用它在新的数据实例上做预测了。但是很多初学者不知道该怎样做好这一点,我经常能看到下面这样的问题:

“我应该如何用Keras对我的模型作出预测?”

在本文中,你会学到如何使用Keras这个Python库完成深度学习模型的分类与回归预测。

看完这篇教程,你能掌握以下几点:

  • 如何确定一个模型,为后续的预测做准备
  • 如何用Keras对分类问题进行类及其概率的预测
  • 如何用Keras进行回归预测

现在就让我们开始吧

本文结构

教程共分为三个部分,分别是:

  • 模型确定
  • 分类预测
  • 回归预测

模型确定

在做预测之前,首先得训练出一个最终的模型。你可能选择k折交叉验证或者简单划分训练/测试集的方法来训练模型,这样做的目的是为了合理估计模型在样本集之外数据上的表现(新数据)

当评估完成,这些模型存在的目的也达到了,就可以丢弃他们。接下来,你得用所有的可用数据训练出一个最终的模型。关于这方面的内容,你可以在下面这个文章中得到更多的信息:

https://machinelearningmastery.com/train-final-machine-learning-model/

分类预测

对于分类问题,模型学习的是一个输入特征到输出特征之间的映射,这里的输出即为一个标签。比如“垃圾邮件”和“非垃圾邮件”

下边是Keras中为简单的二分类问题开发的神经网络模型的一个例子。如果说你以前没有接触过用Keras开发神经网络模型的话,不妨先看看下边这篇文章:

https://machinelearningmastery.com/tutorial-first-neural-network-python-keras/

# 训练一个最终分类的模型

from keras.models import Sequential

from keras.layers import Dense

from sklearn.datasets.samples_generator import make_blobs

from sklearn.preprocessing import MinMaxScaler

# 生成一个二分类问题的数据集

X, y=make_blobs(n_samples=100, centers=2, n_features=2, random_state=1)

scalar=MinMaxScaler()

scalar.fit(X)

X=scalar.transform(X)

# 定义并拟合模型

model=Sequential()

model.add(Dense(4, input_dim=2, activation='relu'))

model.add(Dense(4, activation='relu'))

model.add(Dense(1, activation='sigmoid'))

model.compile(loss='binary_crossentropy', optimizer='adam')

model.fit(X, y, epochs=200, verbose=0)

建立好这个模型后,可能需要将它保存到文件中(比如通过Keras的相关API)。以后你就可以随时加载这个模型,并用它进行预测了。有关这方面的示例,可以参考下边的文章:

https://machinelearningmastery.com/save-load-keras-deep-learning-models/

为了本文的结构更简洁,我们的例子中省去了这个步骤。

继续说回到分类预测的问题。我们希望最终得到的模型能进行两种预测:一是判断出类别,二是给出属于相应类别概率。

  • 类预测

一个类别预测会给定最终的模型以及若干数据实例,我们利用模型来判断这些实例的类别。对于新数据,我们不知道输出的是什么结果,这就是为什么首先需要一个模型。

在Keras中,可以利用predict_class()函数来完成我们上述所说的内容----即利用最终的模型预测新数据样本的类别。

需要注意的是,这个函数仅适用于Sequential模型,不适于使用功能式API开发的模型。(not those models developed using the functional API.)

比如,我们在名为Xnew的数组中有若干个数据实例,它被传入predict_classes()函数中,用来对这些数据样本的类别进行预测。

Xnew=[[...], [...]]

ynew=model.predict_classes(Xnew)

让我们用一个更具体的例子来说明:

# 建立一个新的分类模型

from keras.models import Sequential

from keras.layers import Dense

from sklearn.datasets.samples_generator import make_blobs

from sklearn.preprocessing import MinMaxScaler

# 生成二分类数据集

X, y=make_blobs(n_samples=100, centers=2, n_features=2, random_state=1)

scalar=MinMaxScaler()

scalar.fit(X)

X=scalar.transform(X)

# 定义并拟合最终模型

model=Sequential()

model.add(Dense(4, input_dim=2, activation='relu'))

model.add(Dense(4, activation='relu'))

model.add(Dense(1, activation='sigmoid'))

model.compile(loss='binary_crossentropy', optimizer='adam')

model.fit(X, y, epochs=500, verbose=0)

# 新的未知数据实例

Xnew, _=make_blobs(n_samples=3, centers=2, n_features=2, random_state=1)

Xnew=scalar.transform(Xnew)

# 作出预测

ynew=model.predict_classes(Xnew)

# 显示输入和输出

for i in range(len(Xnew)):

print("X=%s, Predicted=%s" % (Xnew[i], ynew[i]))

下面是对三个实例预测的结果,我们将数据和预测结果一并输出:

X=[0.89337759 0.65864154], Predicted=[0]

X=[0.29097707 0.12978982], Predicted=[1]

X=[0.78082614 0.75391697], Predicted=[0]

如果你只有一个新的实例,那就需要将它包装一下,变成一个数组的形式。以便传给predict_classes()函数,比如这样:

from keras.models import Sequential

from keras.layers import Dense

from sklearn.datasets.samples_generator import make_blobs

from sklearn.preprocessing import MinMaxScaler

from numpy import array

# 生成一个二分类数据集

X, y=make_blobs(n_samples=100, centers=2, n_features=2, random_state=1)

scalar=MinMaxScaler()

scalar.fit(X)

X=scalar.transform(X)

# 定义并拟合最终的新模型

model=Sequential()

model.add(Dense(4, input_dim=2, activation='relu'))

model.add(Dense(4, activation='relu'))

model.add(Dense(1, activation='sigmoid'))

model.compile(loss='binary_crossentropy', optimizer='adam')

model.fit(X, y, epochs=500, verbose=0)

# 未知的新实例

Xnew=array([[0.89337759, 0.65864154]])

# 作出预测

ynew=model.predict_classes(Xnew)

# 显示输入输出

print("X=%s, Predicted=%s" % (Xnew[0], ynew[0]))

运行上边这个例子,会得到对这个单独实例的预测结果

X=[0.89337759 0.65864154], Predicted=[0]

  • 关于类别标签的注意事项

准备数据时,应该将其中的类别标签转换为整数表示(比如原始数据类别可能是一个字符串),这时候你就可能会用到sklearn中的LabelEncoder。

http://scikitlearn.org/stable/modules/generated/sklearn.preprocessing.LabelEncoder.html#sklearn.preprocessing.LabelEncoder

当然,在我们使用LabelEcoder中的函数inverse_transform()时,还可以将那些整数表示的类别标签转换回去。

因为这个原因,在拟合最终模型时,你可能想要保存用于编码y值的LabelEncoder结果。

概率预测

另外一种是对数据实例属于某一类的可能性进行预测。它被称为“概率预测”,当给定一个新的实例,模型返回该实例属于每一类的概率值。(0-1之间)

在Keras中,我们可以调用predict_proba()函数来实现。举个例子:

Xnew=[[...], [...]]

ynew=model.predict_proba(Xnew)

在二分类问题下,Sigmoid激活函数常被用在输出层,预测概率是数据对象属于类别1的可能性,或者属于类别0的可能性(1-概率)

在多分类问题下,则是softmax激活函数常被用在输出层。数据对象属于每一个类别的概率作为一个向量返回。

下边的例子对Xnew数据数组中的每个样本进行概率预测。

from keras.models import Sequential

from keras.layers import Dense

from sklearn.datasets.samples_generator import make_blobs

from sklearn.preprocessing import MinMaxScaler

# 生成二分类数据集

X, y=make_blobs(n_samples=100, centers=2, n_features=2, random_state=1)

scalar=MinMaxScaler()

scalar.fit(X)

X=scalar.transform(X)

# 定义并拟合出最终模型

model=Sequential()

model.add(Dense(4, input_dim=2, activation='relu'))

model.add(Dense(4, activation='relu'))

model.add(Dense(1, activation='sigmoid'))

model.compile(loss='binary_crossentropy', optimizer='adam')

model.fit(X, y, epochs=500, verbose=0)

# 新的未知数据

Xnew, _=make_blobs(n_samples=3, centers=2, n_features=2, random_state=1)

Xnew=scalar.transform(Xnew)

# 做预测

ynew=model.predict_proba(Xnew)

# 显示输入输出

for i in range(len(Xnew)):

print("X=%s, Predicted=%s" % (Xnew[i], ynew[i]))

我们运行这个实例,并将输入数据及这些实例属于类别1的概率打印出来:

X=[0.89337759 0.65864154], Predicted=[0.0087348]

X=[0.29097707 0.12978982], Predicted=[0.82020265]

X=[0.78082614 0.75391697], Predicted=[0.00693122]

回归预测

回归预测是一个监督学习问题,该模型学习一个给定输入样本到输出数值的映射。比如会输出0.1或0.2这样的数字。

下边是一个Keras回归的模型。

# 训练一个回归模型的例子

from keras.models import Sequential

from keras.layers import Dense

from sklearn.datasets import make_regression

from sklearn.preprocessing import MinMaxScaler

# 生成回归数据集

X, y=make_regression(n_samples=100, n_features=2, noise=0.1, random_state=1)

scalarX, scalarY=MinMaxScaler(), MinMaxScaler()

scalarX.fit(X)

scalarY.fit(y.reshape(100,1))

X=scalarX.transform(X)

y=scalarY.transform(y.reshape(100,1))

# 定义并拟合模型

model=Sequential()

model.add(Dense(4, input_dim=2, activation='relu'))

model.add(Dense(4, activation='relu'))

model.add(Dense(1, activation='linear'))

model.compile(loss='mse', optimizer='adam')

model.fit(X, y, epochs=1000, verbose=0)

我们可以在最终的模型中调用predict()函数进行数值的预测。该函数以若干个实例组成的数组作为输入参数。

下面的例子演示了如何对未知的多个数据实例进行回归预测。

from keras.models import Sequential

from keras.layers import Dense

from sklearn.datasets import make_regression

from sklearn.preprocessing import MinMaxScaler

# 生成回归数据集

X, y=make_regression(n_samples=100, n_features=2, noise=0.1, random_state=1)

scalarX, scalarY=MinMaxScaler(), MinMaxScaler()

scalarX.fit(X)

scalarY.fit(y.reshape(100,1))

X=scalarX.transform(X)

y=scalarY.transform(y.reshape(100,1))

# 定义并拟合模型

model=Sequential()

model.add(Dense(4, input_dim=2, activation='relu'))

model.add(Dense(4, activation='relu'))

model.add(Dense(1, activation='linear'))

model.compile(loss='mse', optimizer='adam')

model.fit(X, y, epochs=1000, verbose=0)

# 未知的新数据

Xnew, a=make_regression(n_samples=3, n_features=2, noise=0.1, random_state=1)

Xnew=scalarX.transform(Xnew)

# 作出预测

ynew=model.predict(Xnew)

# 显示输入输出

for i in range(len(Xnew)):

print("X=%s, Predicted=%s" % (Xnew[i], ynew[i]))

运行上面那个多分类预测实例,然后将输入和预测结果并排打印,进行对比。

X=[0.29466096 0.30317302], Predicted=[0.17097184]

X=[0.39445118 0.79390858], Predicted=[0.7475489]

X=[0.02884127 0.6208843 ], Predicted=[0.43370453]

同样的,这个函数可以用于单独实例的预测,前提是它们包装成适当的格式。

举例说明:

from keras.models import Sequential

from keras.layers import Dense

from sklearn.datasets import make_regression

from sklearn.preprocessing import MinMaxScaler

from numpy import array

# 生成回归数据集

X, y=make_regression(n_samples=100, n_features=2, noise=0.1, random_state=1)

scalarX, scalarY=MinMaxScaler(), MinMaxScaler()

scalarX.fit(X)

scalarY.fit(y.reshape(100,1))

X=scalarX.transform(X)

y=scalarY.transform(y.reshape(100,1))

# 定义并拟合模型

model=Sequential()

model.add(Dense(4, input_dim=2, activation='relu'))

model.add(Dense(4, activation='relu'))

model.add(Dense(1, activation='linear'))

model.compile(loss='mse', optimizer='adam')

model.fit(X, y, epochs=1000, verbose=0)

# 新的数据

Xnew=array([[0.29466096, 0.30317302]])

# 作出预测

ynew=model.predict(Xnew)

# 显示输入输出

print("X=%s, Predicted=%s" % (Xnew[0], ynew[0]))

运行实例并打印出结果:

X=[0.29466096 0.30317302], Predicted=[0.17333156]

延伸阅读

这部分提供了一些相关的资料,如果你想更深入学习的话可以看一看。

How to Train a Final Machine Learning Model:

https://machinelearningmastery.com/train-final-machine-learning-model/

Save and Load Your Keras Deep Learning Models:

https://machinelearningmastery.com/save-load-keras-deep-learning-models/

Develop Your First Neural Network in Python With Keras Step-By-Step:

https://machinelearningmastery.com/tutorial-first-neural-network-python-keras/

The 5 Step Life-Cycle for Long Short-Term Memory Models in Keras:

https://machinelearningmastery.com/5-step-life-cycle-long-short-term-memory-models-keras/

How to Make Predictions with Long Short-Term Memory Models in Keras:

https://machinelearningmastery.com/make-predictions-long-short-term-memory-models-keras/

总结:

在本教程中,你知道了如何使用Keras库通过最终的深度学习模型进行分类和回归预测。

具体来说,你了解到:

  • 如何确定一个模型,为后续的预测做准备
  • 如何用Keras对分类问题进行类及其概率的预测
  • 如何用Keras进行回归预测

对本文的内容有什么问题吗?在下面的评论中提出来,我将尽我所能来回答。

原文链接:

https://machinelearningmastery.com/how-to-make-classification-and-regression-predictions-for-deep-learning-models-in-keras/

译者简介

张逸,中国传媒大学大三在读,主修数字媒体技术。对数据科学充满好奇,感慨于它创造出来的新世界。目前正在摸索和学习中,希望自己勇敢又热烈,学最有意思的知识,交最志同道合的朋友。

自Medium

机器之心编译

参与:Nurhachu、蒋思源、吴攀

无监督学习是深度学习的圣杯。它的目的是希望能够用极少量且不需要标注的数据训练通用系统。本文将从无监督学习的基本概念开始再进一步简述无监督学习的各个基础算法及其优缺点。本文作者为专注机器人与视觉研究的 e-Lab 的研究者 Eugenio Culurciello。

如今深度学习模型都需要在大规模的监督数据集上训练。这意味着对于每一个数据,都会有一个与之对应的标签。在很流行的 ImageNet 数据集中,其共有一百万张带人工标注的图片,即 1000 类中的每一类都有 1000 张。创建这么一个数据集是需要一番功夫的,可能需要很多人花数月的功夫完成。假定现在要创建一个有一百万类的数据集,那么就必须给总共 1 亿帧视频数据集中的每一帧做标注,这基本是无法实现的。

现在,回想一下在你很小的时候,自己是如何得到教导的。没错,我们的确受到了一些监督,但是当你的父母告诉你这是一只「猫」之后,他们不会在日后的生活中每一次观察到猫时都告诉你这是「猫」!而如今的监督式学习是这样的:我一次又一次地告诉你「猫」是什么样的,也许会重复一百万次。然后你的深度学习模型就领会了关于猫的知识。

理想情况下,我们希望有一个更像我们的大脑一样去运行得模型。它仅仅需要很少的一些标签就能够理解现实世界中的很多类事物。在现实世界中,我指的类是物体类别、动作类别、环境类别、物体的部分的类别,诸如此类还有很多很多。

正如你会在这篇评论中看到的一样,最成功的模型就是那些能够预测视频中即将出现的画面的模型。很多这类技术面临并正尝试解决的一个问题,即为了达到良好的全局表现,训练要在视频上进行,而不是静态的图片上。这是将学习到的表征应用在实际任务中的唯一途径。

基本概念

无监督学习研究的主要目标就是要预训练出能够用于其他任务的模型(即鉴别器或者编码器)。编码器的特点应该尽可能的通用,以便可以用在分类任务中(如在 ImageNet 上进行训练),并且提供尽可能像监督模型一样好的结果。

最新的监督模型总是表现得比无监督预训练模型更好。那是因为,监督会允许模型能够更好的编码数据集上的特征。但是当模型应用在其他的数据集上时,监督会衰减。在这方面,无监督训练有希望提供更加通用的特性来执行任何任务。

如果以实际生活中的应用为目标,例如无人驾驶、动作识别、目标检测和实时提取中的识别,那么算法需要在视频上训练。

自编码器

UC Davis 的 Bruno Olshausen 和康奈尔大学的 David Field 于 1996 年的发表的论文《Sparse Coding with an Overcomplete Basis Set: A Strategy by V1?》(论文链接:http://redwood.psych.cornell.edu/papers/olshausen_field_1997.pdf) 表明,编码理论可以被用在视觉皮层的接收域中。他们证明了我们大脑中的基本视觉旋涡 (V1) 使用稀疏性原理来创建一个能够被用于重建输入图像的基本功能的最小集合。

下面的链接是 2014 年伦敦微软 Bing 团队的 Piotr Mirowski 关于自动编码器的一个很好的综述。

链接:https://piotrmirowski.files.wordpress.com/2014/03/piotrmirowski_2014_reviewautoencoders.pdf

Yann LeCun 的团队也从事这个领域的研究。在链接网页中的 demo 中,你可以看到像 V1 一样的滤波器是如何学习的。(链接:http://www.cs.nyu.edu/~yann/research/deep/)

通过重复贪婪逐层训练的过程,堆栈式自编码器(Stacked-auto encoder)也被使用了。

自动编码器方法也被称为直接映射方法。

自动编码器/稀疏编码/堆叠式自动编码的优点和缺点

优点:

  • 简单的技术:重建输入

  • 多层可堆叠

  • 直观和基于神经科学的研究

缺点

  • 每一层都被贪婪地(greedily)训练

  • 没有全局优化

  • 比不上监督学习地性能

  • 多层失效

  • 对通用目标地表征而言,重建输入可能不是理想的指标

聚类学习

它是用 k-means 聚类在多层中学习滤波器的一种技术。

我们组把这项技术命名为:聚类学习(见论文:Clustering Learning for Robotic Vision)、聚类连接 (见论文:An Analysis of the Connections Between Layers of Deep Neural Networks),以及卷积聚类 (见论文:Convolutional Clustering for Unsupervised Learning)。就在最近,这项技术在流行地无监督学习数据集 STL-10 上实现了非常好的结果。

我们在这个领域的研究和 Adam Coates 与 Andrew Ng 在基于 k-means 学习特征表示 ( Learning Feature Representations with K-means ) 中发表的研究成果是独立的。

众所周知,由于在求解配分函数时的数值问题,受限波尔兹曼机(RBM),深波尔兹曼机(DBM),深度信念网络(DBN/参见 Geoffrey E. Hinton 等人的研究:A fast learning algorithm for deep belief net)等模型已经很难去训练了。因此,它们没有广泛应用于解决问题中。

聚类学习的优缺点:

优点:

  • 简单的技术:得到相似群集的输出

  • 多层可堆叠

  • 直观和基于神经科学的研究

缺点:

  • 每一层都被贪婪地训练

  • 没有全局优化

  • 在某些情况下可以和监督学习的性能媲美

  • 多层递增式失效==性能回报递减

生成对抗网络模型

生成对抗网络尝试通过鉴别器和生成器的对抗而得来一个优良的生成模型,该网络希望能够生成足以骗过鉴别器的逼真图像。生成模型这一领域近年来十分优秀的生成对抗网络正是由 Ian Goodfellow 和 Yoshua Bengio 等人在论文《Generative Adversarial Nets》中提出。这里还有 OpenAI 的研究员 Ian 在 2016 年底做的关于生成对抗网络 (GANS) 的总结,视频链接:https://channel9.msdn.com/Events/Neural-Information-Processing-Systems-Conference/Neural-Information-Processing-Systems-Conference-NIPS-2016/Generative-Adversarial-Networks。

由 Alec Radford、 Luke Metz 以及 Soumith Chintala 等人实例化的一个被称作 DCGAN 的生成对抗模型取得了非常好的结果。他们的研究发表在论文:Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks。

Vincent Dumoulin 和 Ishmael Belghazi 等人对这个模型做了一个比较好的解释(链接:https://ishmaelbelghazi.github.io/ALI/)。

DCGAN 鉴别器被设计来判断一副输入图片是真实的(来源于某个数据集的真实图片)或虚假的(来源于某个生成器)。生成器将随机地噪声向量(例如 1024 个数值)作为输入,并生成一副图片。

在 DCGAN 中,生成器网络如下:

尽管这个鉴别器是一个标准的神经网络。具体的细节可以参考下文提及的代码。

关键是要并行地去训练这两个网络,同时不要完全过拟合,因此才会复制数据集。学习到的特征需要泛化在未知的样本上,所以学习数据集将不会有用。

在 Torch7 上训练 DCGAN 的代码(https://github.com/soumith/dcgan.torch)也被提供了。这需要大量的实验,相关内容 Yann LeCun 在 Facebook 中也分享过:https://www.facebook.com/yann.lecun/posts/10153269667222143

当生成器和鉴别器都被训练之后,你可以同时使用两者。主要的目标就是训练出一个能够被用于其他任务的鉴别器网络,例如在其他数据集上可以分类。生成器可以用来从随机向量中生成图片。这些图片有着非常有趣的属性。首先,它们从输入空间中提供了平滑的变换。如下所示的例子展示了在 9 个随机输入向量中移动而生成的图片:

输入向量空间也提供了数学属性,证明学习到的特征是按照相似性来组织的,如下图所示:

生成器学习到的平滑空间启示鉴别器也要有类似的属性,这使得鉴别器在编码图像时成了一个很棒的通用特征提取器。这有助于解决 CNN 在训练不连续图像的时候由于对抗噪声而造成的失败(详见 Christian Szegedy 等人的文章《Intriguing properties of neural networks》,https://arxiv.org/abs/1312.6199)。

GAN 最新的进展,在仅有 1000 个标签样本的 CIFAR-10 数据集上实现了 21% 的错误率,参见 OpenAI 的 Tim Salimans 等人的论文《Improved Techniques for Training GANs》,论文链接:https://arxiv.org/pdf/1606.03498v1.pdf。

最近关于 infoGAN 的论文《InfoGAN: Interpretable Representation Learning by Information Maximizing Generative Adversarial Nets》(链接:https://arxiv.org/abs/1606.03657)中,能够产生特征非常清晰的图像,并且这些图像具有更加有趣的意义。然而,他们并没有公布学习到的特征在某项任务或某个数据集中的性能对比。

在如下所示的博客和网站中也有关于生成对抗模型的总结,参见 OpenAI 的技术博客 https://blog.openai.com/generative-models/ 和网页 https://code.facebook.com/posts/1587249151575490/a-path-to-unsupervised-learning-through-adversarial-networks/。

另一个非常有趣的例子如下,在例子中,作者用生成对抗训练去学习从文本描述中生成图像。参见论文《Generative Adversarial Text to Image Synthesis》,链接:https://arxiv.org/abs/1605.05396。

我最欣赏这项工作的地方在于它所使用的网络用文本描述作为生成器的输入,而不是随机向量,这样就可以精确地控制生成器的输出。网络模型结构如下图所示:

生成对抗模型的缺点和优点

优点:

  • 对整个网络的全局训练

  • 易于编程和实现

缺点:

  • 难以训练和转换问题

  • 在某些情况下可以比得上监督学习的性能

  • 需要提升可用性(这是所有无监督学习算法面临的问题)

可以从数据中学习的模型

通过设计不需要标签的无监督学习任务和旨在解决这些任务的学习算法,这些模型直接从无标签的数据中学习。

在视觉表征中通过解决拼图问题来进行无监督学习确实是一个聪明的技巧。作者将图像分割成了拼图,并且训练深度网络来解决拼图问题。最终得到的网络的表现足以比肩最好的预训练网络。详见论文《Unsupervised Learning of Visual Representations by Solving Jigsaw Puzzles》,链接:https://arxiv.org/abs/1603.09246

在视觉表征中通过图像补丁和布局来进行无监督学习也是一个聪明的技巧。他们让同一幅图像上的两个补丁紧密分布。这些补丁在统计上来讲是同一个物体。第三个补丁选择随机的图像,并且布局在随机的位置,从统计上来讲与前两个补丁并不是同一类物体。然后训练一个深度网络来区分两个属于同一类的补丁和另一个不同类别的补丁。最终得到的网络具有和最高性能精调网络之一相同的性能。详情参见论文《Learning visual groups from co-occurrences in space and time》,链接:https://arxiv.org/abs/1511.06811。

从立体图像重建中进行的无监督学习模型采用立体图像作为输入,例如图像一帧的左半部分,然后重建出图像的右半部分。虽然这项工作并不针对无监督学习,但是它可以用作无监督学习。这种方法也可以用来从静态图片生成 3D 电影。参见论文《Deep3D: Fully Automatic 2D-to-3D Video Conversion with Deep Convolutional Neural Networks》,链接:https://arxiv.org/abs/1604.03650,github 上的 Python 源码:https://github.com/piiswrong/deep3d。

利用替代类别的无监督学习视觉表征使用图像不行来创建非常大的替代类。这些图像补丁然后被增强,然后被用来训练基于增强替代类的监督网络。这在无监督特征学习中给出了最好的结果。详情参见论文《Discriminative Unsupervised Feature Learning with Exemplar Convolutional Neural Networks》,链接:https://arxiv.org/abs/1406.6909。

使用视频的无监督学习视觉表征采用了基于 LSTM 的编码器-解码器对。编码 LSTM 运行在视频帧的序列上以生成一个内部表征。随后这些表征通过另一个 LSTM 被解码以生成一个目标序列。为了使这个变成无监督的,一种方法是预测与输入相同的序列。另一种方式是预测未来的帧。详情参见论文《Unsupervised Learning of Visual Representations using Videos》,链接:https://arxiv.org/abs/1505.00687。

另一篇使用视频的文章出自 MIT 的 Vondrick 和 Torralba 等人(http://arxiv.org/abs/1504.08023),有着非常惹人注目的结果。这项工作背后的思想是从视频输入中预测未来帧的表示。这是一种优雅的方法。使用的模型如下:

这项技术的一个问题就是:一个训练在静态图像帧上的神经网络被用来解释视频输入。这种网络不会学习到视频的时间动态变化以及在空间运动的物体的平滑变换。所以我们认为这个网络并不适合用来预测未来视频中的画面。

为了克服这个问题,我们团队创建了一个大型的视频数据集 eVDS(https://engineering.purdue.edu/elab/eVDS/),可用来直接从视频数据上训练新的(递归和反馈)网络模型。

PredNet

PredNet 是被设计来预测视频中未来帧的网络。在这个博客中可以看到一些例子,博客链接:https://coxlab.github.io/prednet/。

PredNet 是一个非常聪明的神经网络型,在我们看来,它将在将来的神经网络中起着重要的作用。PredNet 学习到了超越监督式 CNN 中的单帧图片的神经表征。

PredNet 结合了生物启发的双向 [人脑模型](详见论文《Unsupervised Pixel-prediction》,https://papers.nips.cc/paper/1083-unsupervised-pixel-prediction.pdf)。它使用了 [预测编码和神经模型中的反馈连接](详见论文《Neural Encoding and Decoding with Deep Learning for Dynamic Natural Vision》,http://arxiv.org/abs/1608.03425)。下面是 PredNet 模型以及一个具有两个堆叠层的例子:

PredNet 结合了生物启发的双向人脑模型

这个模型有以下这几个优点:

  • 可使用无标签的数据来训练

  • 在每一层嵌入了损失函数来计算误差

  • 具有执行在线学习的能力,通过监控错误信号,当模型不能预测输出的时候,它会知道需要学习误差信号

PredNet 存在的一个问题是,对第一层的一些简单的基于运动的滤波器而言,预测未来输入的帧是相对容易的。在我们所做的 PredNet 的实验中,PredNet 在重建输入帧的时候学会了在重建输入帧时达到很好的效果,但是更高层不会学到较好的表征。事实上,在实验中更高层连简单的分类任务都解决不了。

事实上,预测未来的帧是不必要的。我们愿意做的就是去预测下一帧的表征,就像 Carl Vondrick 做的一样。详见论文《Anticipating Visual Representations from Unlabeled Video》,链接:https://arxiv.org/abs/1504.08023。

通过观察物体的运动来学习特征

最近的这篇论文通过观察视频中物体的运动来训练无监督模型(《Learning Features by Watching Objects Move》,https://people.eecs.berkeley.edu/~pathak/unsupervised_video/)。运动以光流的形式被提取出来,并被用作运动物体的分割模板。尽管光流信号并没有提供任何一个接近良好的分割模板,但是在大规模数据集上的平均效果使得最终的网络会表现良好。例子如下所示:

这项工作是非常激动人心的,因为它遵循关于人类视觉皮层如何学习分割运动物体的神经学理论。参见论文《Development of human visual function》,链接:http://www.sciencedirect.com/science/article/pii/S004269891100068X。

未来

未来需要你们来创造。

无监督训练仍然还是一个有待发展的主题,你可以通过以下方式做出较大的贡献:

  • 创建一个新的无监督任务去训练网络,例如:解决拼图问题、对比图像补丁、生成图像等......

  • 想出能够创造很棒的无监督功能的任务,例如:像我们人类视觉系统的工作方式一样,理解立体图像和视频中什么是物体、什么是背景。