gRPC在CPU-FPGA异构系统上的应用与展望

这篇具有很好参考价值的文章主要介绍了gRPC在CPU-FPGA异构系统上的应用与展望。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

0 本文主体内容与行文组织

本文使用gRPC框架简单实现了CPU-FPGA的异构系统关于矩阵乘法的运算,通过一个小的benchmark我们可以很直观地看到让具有特性的硬件去完成相关的运算,可以高效提升我们运算速率(本文提供的案例提升了8倍的计算速度)。文章是基于中科大孟老师的授课内容与笔者目前关注的一个小领域的简单结合。本文主要是分为三大部分,第一部分简单介绍了gRPC协议、第二部分介绍了具体的实现、第三部分是关于本文的总结以及鸣谢。

1 背景及介绍

1.1 什么是gRPC

wiki百科如是介绍:

gRPC 一开始由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。

因此在了解gRPC之前,我们需要先了解一下RPC。
RPC(Remote Procedure Call),直译为中文就是远程过程调用,是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一个地址空间(通常为一个开放网络的一台计算机)的子程序,而程序员就像调用本地程序一样,无需额外地为这个交互作用编程(无需关注细节)。RPC是一种服务器-客户端(Client/Server)模式,经典实现是一个通过发送请求-接受回应进行信息交互的系统。
详细介绍可以点此参考官方文档

**RPC 架构包含4个核心组件:**客户端(Client)、客户端存根(Client Stub)、服务端(Server)及服务端存根(Server Stub)。
(1)客户端:服务的调用者。
(2)客户端存根:存放服务端的服务列表,将客户端请求打包并通过网络发送到服务端。
(3)服务端:服务提供者。
(4)服务端存根:接收客户端消息并解包,然后调用本地的方法。
gRPC在CPU-FPGA异构系统上的应用与展望,fpga开发,rpc
整个流程可以概括如下:

(1)客户端以本地调用的方式发起调用,这时调用的其实是客户端存根。

(2)服务端存根在收到调用后,负责将被调用的方法名、参数等打包并编码成特定格式的能进行网络传输的消息体。

(3)客户端存根将消息体通过网络发送给服务端。

(4)服务端存根通过网络接收到消息,按照相应的格式进行拆包、解码,获取方法名和参数。

(5)服务端存根根据方法名和参数进行本地调用,这时调用的是真正的服务提供者。

(6)服务提供者调用本地服务,然后将结果返回给服务端存根。

(7)服务端存根将返回值打包并编码成消息。

(8)服务端存根通过网络将消息发送给客户端。

(9)服务端存根在收到消息后,进行拆包、解码并返回给客户端。

(10)服务端存根得到本次RPC调用的最终结果。
在RPC中一般会用到动态代理、序列化反序列化、NIO网络通信、服务注册和发现等技术。

前文已经简单描述了RPC这种协议,而本文着重探讨的,则是Google基于RPC协议开发出来的框架gRPC,它是基于 ProtoBuf 序列化协议进行开发,支持多种语言(Golang、Python、Java等)。

gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,能够更容易地创建分布式应用和服务。与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。
gRPC在CPU-FPGA异构系统上的应用与展望,fpga开发,rpc

1.2 gRPC的使用

这部分可以参考中科大孟老师的文档,老师的文档非常详细实用,这里我不再画蛇添足。
孟老师的gRPC_Protobuf 快速编程指南
如果需要获取更为详细的文档信息,可以点此参阅官方文档

2 gRPC在CPU-FPGA上的使用

2.1 背景介绍

作为一个用户,我可能不想去关注具体的功能实现,而只是需要调用某个函数来完成我的需求,所以这个场景就相当合适使用gRPC来完成相关的计算。 比如我想完成一个矩阵的计算(由于个人水平原因,暂时只能实现这个),就可以在FPGA上完成相关的硬件布线资源然后在电脑上机进行相关的函数调用来完成计算。
gRPC在CPU-FPGA异构系统上的应用与展望,fpga开发,rpc
经过我对gRPC这个框架的进一步了解,我发现它的使用更多是基于分布式框架的应用,刚好我实验室有块ZYNQ7010的FPGA板卡,因此我想尝试一下它的异构调用功能。FPGA是现场可编程门阵列。

现代的我们所使用的电脑的CPU指令是串行执行的,因为现在的计算机体系结构当中的指令流水线(Pipeline)是按照顺序执行的,当然,多核CPU虽然可以并行执行多个进程或线程,但本质上,仍然是基于串行的流水线。

而本文介绍的FPGA基于它特殊的结构,即可以并行执行一些计算,针对这一特性,我设计了一个16 × 16的脉动阵列块,来用于本文实例当中的两个16 × 16的矩阵块的计算,来探讨gRPC在异构系统当中通信的作用。

2.2 相关前置知识

FPGA(现场可编程门阵列) 拥有大量的“逻辑门”资源与布线资源,最基本的逻辑门如我们熟悉的与门,或门,非门,异或门。用户可以通过编写硬件verilog语言经过vivadoquarts综合生成对应的硬件电路,传统的CPUASIC芯片的硬件逻辑在出厂后就已经固定下来了,而FPGA可以多次擦写以实现不同的硬件逻辑。(个人理解,可能有些偏颇)

脉动阵列是一种二维硬件结构,其基本组成单元是一个乘法器和累加寄存器组成的计算单元(MAC),MAC 之间在水平和竖直方向上相互连接形成二维阵列。两个矩阵的输入数据从水平方向和竖直方向上流入,经过 n 个周期后在另一端得到计算结果
gRPC在CPU-FPGA异构系统上的应用与展望,fpga开发,rpc
可以构造如下的脉动阵列,矩阵A的每一行从左侧输入,矩阵B的每一列元素向下输入,比如第一个cycle之后,元素
a00与元素·b00装入处理单元c00,第二个周期执行乘法然后存储在处理单元当中,元素a00与元素b00分别向左和向右流动,这样相比于传统的计算,矩阵元素不用重复加载,且计算可以并行进行,如下图3×3的矩阵而言,总体只需要6 cycle就能完成计算。
gRPC在CPU-FPGA异构系统上的应用与展望,fpga开发,rpc

2.3 核心源代码的实现

2.3.1 硬件布线的实现

block.v单元设计,这个单元主要负责乘法和加法

module pe(inp_north, inp_west, clk, rst, outp_south, outp_east, result);
	input [31:0] inp_north, inp_west;
	output reg [31:0] outp_south, outp_east;
	input clk, rst;
	output reg [63:0] result;
	wire [63:0] multi;
	always @(posedge rst or posedge clk) begin
		if(rst) begin
			result <= 0;
			outp_east <= 0;
			outp_south <= 0;
		end
		else begin
			result <= result + multi;
			outp_east <= inp_west;
			outp_south <= inp_north;
		end
	end
	assign multi = inp_north*inp_west;
endmodule

16 × 16的脉动阵列块
避免文章繁琐,这里仅给出核心代码块,部分代码重复较多已经省略。

//16 * 16 个block模块的实例化 
//16 * 16 个block模块的实例化 
`include "block.v"
module systolic_array(
              clk, rst, done);
    //from north and west
    block P0 (inp_north0, inp_west0, clk, rst, outp_south0, outp_east0, result0);
    block P1 (inp_north1, outp_east0, clk, rst, outp_south1, outp_east1, result1);
    block P2 (inp_north2, outp_east1, clk, rst, outp_south2, outp_east2, result2);
    block P3 (inp_north3, outp_east2, clk, rst, outp_south3, outp_east3, result3);
    block P4 (inp_north4, outp_east3, clk, rst, outp_south4, outp_east4, result4);
    block P5 (inp_north5, outp_east4, clk, rst, outp_south5, outp_east5, result5);
    block P6 (inp_north6, outp_east5, clk, rst, outp_south6, outp_east6, result6);
    block P7 (inp_north7, outp_east6, clk, rst, outp_south7, outp_east7, result7);
    block P8 (inp_north8, outp_east7, clk, rst, outp_south8, outp_east8, result8);
    block P9 (inp_north9, outp_east8, clk, rst, outp_south9, outp_east9, result9);
    block P10 (inp_north10, outp_east9, clk, rst, outp_south10, outp_east10, result10);
    block P11 (inp_north11, outp_east10, clk, rst, outp_south11, outp_east11, result11);
    block P12 (inp_north12, outp_east11, clk, rst, outp_south12, outp_east12, result12);
    block P13 (inp_north13, outp_east12, clk, rst, outp_south13, outp_east13, result13);
    block P14 (inp_north14, outp_east13, clk, rst, outp_south14, outp_east14, result14);
    block P15 (inp_north15, outp_east14, clk, rst, outp_south15, outp_east15, result15);
    //from west
    block p16 (outp_south0, inp_west16, clk, rst, outp_south16, outp_east16, result16);
    block p32 (outp_south16,inp_west32, clk, rst, outp_south32, outp_east32, result32);
    block P16 (outp_south0, inp_west16, clk, rst, outp_south16, outp_east16, result16);
    block P32 (outp_south16, inp_west32, clk, rst, outp_south32, outp_east32, result32);
    block P48 (outp_south32, inp_west48, clk, rst, outp_south48, outp_east48, result48);
    block P64 (outp_south48, inp_west64, clk, rst, outp_south64, outp_east64, result64);
    block P80 (outp_south64, inp_west80, clk, rst, outp_south80, outp_east80, result80);
    block P96 (outp_south80, inp_west96, clk, rst, outp_south96, outp_east96, result96);
    block P112 (outp_south96, inp_west112, clk, rst, outp_south112, outp_east112, result112);
    block P128 (outp_south112, inp_west128, clk, rst, outp_south128, outp_east128, result128);
    block P144 (outp_south128, inp_west144, clk, rst, outp_south144, outp_east144, result144);
    block P160 (outp_south144, inp_west160, clk, rst, outp_south160, outp_east160, result160);
    block P176 (outp_south160, inp_west176, clk, rst, outp_south176, outp_east176, result176);
    block P192 (outp_south176, inp_west192, clk, rst, outp_south192, outp_east192, result192);
    block P208 (outp_south192, inp_west208, clk, rst, outp_south208, outp_east208, result208);
    block P224 (outp_south208, inp_west224, clk, rst, outp_south224, outp_east224, result224);
    block P240 (outp_south224, inp_west240, clk, rst, outp_south240, outp_east240, result240);
    // 2 row

    block P17 (outp_south1, outp_east16, clk, rst, outp_south17, outp_east17, result17);
    block P18 (outp_south2, outp_east17, clk, rst, outp_south18, outp_east18, result18);
    block P19 (outp_south3, outp_east18, clk, rst, outp_south19, outp_east19, result19);
    block P20 (outp_south4, outp_east19, clk, rst, outp_south20, outp_east20, result20);
    block P21 (outp_south5, outp_east20, clk, rst, outp_south21, outp_east21, result21);
    block P22 (outp_south6, outp_east21, clk, rst, outp_south22, outp_east22, result22);
    block P23 (outp_south7, outp_east22, clk, rst, outp_south23, outp_east23, result23);
    block P24 (outp_south8, outp_east23, clk, rst, outp_south24, outp_east24, result24);
    block P25 (outp_south9, outp_east24, clk, rst, outp_south25, outp_east25, result25);
    block P26 (outp_south10, outp_east25, clk, rst, outp_south26, outp_east26, result26);
    block P27 (outp_south11, outp_east26, clk, rst, outp_south27, outp_east27, result27);
    block P28 (outp_south12, outp_east27, clk, rst, outp_south28, outp_east28, result28);
    block P29 (outp_south13, outp_east28, clk, rst, outp_south29, outp_east29, result29);
    block P30 (outp_south14, outp_east29, clk, rst, outp_south30, outp_east30, result30);
    block P31 (outp_south15, outp_east30, clk, rst, outp_south31, outp_east31, result31);
    // 3 row
    // 4 row 
    // 5 row
    // 6 row
    // 7 row
    // // 8 row
    // // 9 row
    // //10 row
    // //11 row
    // //12 row
    // //13 row
    // //14 row
    // //15 row
    //16 row

    always @(posedge clk or posedge rst) begin
        if(rst) begin
            done <= 0;
            count <= 0;
        end
        else begin
            //256个脉冲之后结束运算
			//15 + 16 + 15 = 46个脉冲计算完成
            if(count == 50) begin
                ...
                done <= 1;
                count <= 0;
            end
            else begin
                done <= 0;
                count <= count + 1;
            end
        end 
    end
endmodule    

上述代码经过仿真布线综合之后生成相应的matmul.bit文件在本地,之后烧录到ZYNQ板卡上即可运行相关的功能。使用的软件为vivado 2018.3,仿真软件为ModelsimSE-64

2.3.2 client和server代码实现

首先定义消息格式
matrix_multiplier.proto

syntax = "proto3";

package matrix_multiplier;

service MatrixMultiplier {
  rpc MultiplyMatrices (MatrixRequest) returns (MatrixResponse) {}
}

message MatrixRequest {
  repeated int32 matrix_a = 1;
  repeated int32 matrix_b = 2;
}

message MatrixResponse {
  repeated int32 result = 1;
}

之后在命令行输入

$ protoc --python_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_python_plugin` matrix_multiplier.proto

得到matrix_multiplier_pb2文件和matrix_multiplier_pb2_grpc文件

# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler.  DO NOT EDIT!
# source: matrix_multiplier.proto
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
# @@protoc_insertion_point(imports)

_sym_db = _symbol_database.Default()




DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17matrix_multiplier.proto\x12\x11matrix_multiplier\"3\n\rMatrixRequest\x12\x10\n\x08matrix_a\x18\x01 \x03(\x05\x12\x10\n\x08matrix_b\x18\x02 \x03(\x05\" \n\x0eMatrixResponse\x12\x0e\n\x06result\x18\x01 \x03(\x05\x32m\n\x10MatrixMultiplier\x12Y\n\x10MultiplyMatrices\x12 .matrix_multiplier.MatrixRequest\x1a!.matrix_multiplier.MatrixResponse\"\x00\x62\x06proto3')

_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'matrix_multiplier_pb2', _globals)
if _descriptor._USE_C_DESCRIPTORS == False:

  DESCRIPTOR._options = None
  _globals['_MATRIXREQUEST']._serialized_start=46
  _globals['_MATRIXREQUEST']._serialized_end=97
  _globals['_MATRIXRESPONSE']._serialized_start=99
  _globals['_MATRIXRESPONSE']._serialized_end=131
  _globals['_MATRIXMULTIPLIER']._serialized_start=133
  _globals['_MATRIXMULTIPLIER']._serialized_end=242
# @@protoc_insertion_point(module_scope)

# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc

import matrix_multiplier_pb2 as matrix__multiplier__pb2


class MatrixMultiplierStub(object):
    """Missing associated documentation comment in .proto file."""

    def __init__(self, channel):
        """Constructor.

        Args:
            channel: A grpc.Channel.
        """
        self.MultiplyMatrices = channel.unary_unary(
                '/matrix_multiplier.MatrixMultiplier/MultiplyMatrices',
                request_serializer=matrix__multiplier__pb2.MatrixRequest.SerializeToString,
                response_deserializer=matrix__multiplier__pb2.MatrixResponse.FromString,
                )


class MatrixMultiplierServicer(object):
    """Missing associated documentation comment in .proto file."""

    def MultiplyMatrices(self, request, context):
        """Missing associated documentation comment in .proto file."""
        context.set_code(grpc.StatusCode.UNIMPLEMENTED)
        context.set_details('Method not implemented!')
        raise NotImplementedError('Method not implemented!')


def add_MatrixMultiplierServicer_to_server(servicer, server):
    rpc_method_handlers = {
            'MultiplyMatrices': grpc.unary_unary_rpc_method_handler(
                    servicer.MultiplyMatrices,
                    request_deserializer=matrix__multiplier__pb2.MatrixRequest.FromString,
                    response_serializer=matrix__multiplier__pb2.MatrixResponse.SerializeToString,
            ),
    }
    generic_handler = grpc.method_handlers_generic_handler(
            'matrix_multiplier.MatrixMultiplier', rpc_method_handlers)
    server.add_generic_rpc_handlers((generic_handler,))


 # This class is part of an EXPERIMENTAL API.
class MatrixMultiplier(object):
    """Missing associated documentation comment in .proto file."""

    @staticmethod
    def MultiplyMatrices(request,
            target,
            options=(),
            channel_credentials=None,
            call_credentials=None,
            insecure=False,
            compression=None,
            wait_for_ready=None,
            timeout=None,
            metadata=None):
        return grpc.experimental.unary_unary(request, target, '/matrix_multiplier.MatrixMultiplier/MultiplyMatrices',
            matrix__multiplier__pb2.MatrixRequest.SerializeToString,
            matrix__multiplier__pb2.MatrixResponse.FromString,
            options, channel_credentials,
            insecure, call_credentials, compression, wait_for_ready, timeout, metadata)

matrix_multiplier_client.py

import grpc
import matrix_multiplier_pb2
import matrix_multiplier_pb2_grpc
from pynq import Overlay

def run():
    # 连接到 gRPC 服务器
    channel = grpc.insecure_channel('localhost:99999')
    stub = matrix_multiplier_pb2_grpc.MatrixMultiplierStub(channel)

    # 构造矩阵请求
    matrix_a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    matrix_b = [9, 8, 7, 6, 5, 4, 3, 2, 1]

    request = matrix_multiplier_pb2.MatrixRequest(
        matrix_a=matrix_a,
        matrix_b=matrix_b
    )

    # 发送请求并获取结果
    response = stub.MultiplyMatrices(request)
    print("Result Matrix:", response.result)

if __name__ == '__main__':
    run()

matrix_multiplier_server.py gRPC服务器

import grpc
from concurrent import futures
import matrix_multiplier_pb2
import matrix_multiplier_pb2_grpc
from pynq import Overlay
import time

class MatrixMultiplierServicer(matrix_multiplier_pb2_grpc.MatrixMultiplierServicer):
    def __init__(self):
        super().__init__()
        self.overlay = Overlay("matmul_bitstream.bit")
        self.matrix_multiplier_ip = self.overlay.matrix_multiplier_0

def MultiplyMatrices(self, request, context):
    # 从 gRPC 请求中获取矩阵数据
    matrix_a = request.matrix_a.values
    matrix_b = request.matrix_b.values

    # 将数据传递给硬件矩阵乘法器 IP 核
    self.matrix_multiplier_ip.write(0x10, matrix_a)
    self.matrix_multiplier_ip.write(0x18, matrix_b)

    # 启动硬件计算
    self.matrix_multiplier_ip.write(0x00, 0x01)

    # 等待硬件计算完成
    while not self.matrix_multiplier_ip.read(0x00) & 0x4:
        pass

    # 获取性能计数器值
    cycle_count = self.matrix_multiplier_ip.read(0x4)

    # 打印 FPGA 加速模块的执行周期数
    print(f"FPGA Accelerator Cycle Count: {cycle_count}")

    # 从硬件读取结果
    result = self.matrix_multiplier_ip.read(0x20, 256)  # Assuming a 16x16 matrix result

    return matrix_multiplier_pb2.MatrixResponse(result=result)

def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    matrix_multiplier_pb2_grpc.add_MatrixMultiplierServicer_to_server(
    MatrixMultiplierServicer(), server)
    port = 9999
    server_address = f'[::]:{port}'
    print(f'Starting gRPC server on {server_address}')
    # server.add_insecure_port('localhost:99999')
    server.start()
    server.wait_for_termination()

if __name__ == '__main__':
    serve()

2.4 结果与分析

这里以16 × 16的随机数组相乘并返回结果数组得到如下结果
gRPC在CPU-FPGA异构系统上的应用与展望,fpga开发,rpc
为了做一组对比实验,我简单编写了一个python编写计算程序。

import numpy as np
import timeit

# 定义表达式
setup_code = """
import numpy as np

matrix_a = np.random.randint(1, 10, size=(16, 16))
matrix_b = np.random.randint(1, 10, size=(16, 16))
"""

execution_code = """
matrix_result = np.dot(matrix_a, matrix_b)
"""

# 使用timeit运行代码并测量时间
time_taken = timeit.timeit(execution_code, setup=setup_code, number=100) 

# 输出平均执行时间
print(f"Average execution time: {time_taken / 100 * 1000000000} nanosecond")

可以看到CPU执行这个矩阵运算需要3085 ns,前面的使用FPGA进行相同的运算大概需要46 * 10 = 460 ns,速度提升了8倍左右
gRPC在CPU-FPGA异构系统上的应用与展望,fpga开发,rpc

3 总结

本文使用gRPC框架简单实现了CPU-FPGA的异构系统关于矩阵乘法的运算,通过一个小的benchmark我们可以很直观地看到让具有特性的硬件去完成相关的运算,可以高效提升我们运算速率(本文提供的案例提升了8倍的计算速度)。

虽然该次小实验从我的突发奇想到具体调研花了不少时间,但是还有许多可以进一步提升的地方:

  • 将这个框架手搓为硬件描述语言verilog语言来实现,这是自己一开始的构想,但由于各种条件限制,我发现这是一个非常浩大的工程,所以只能止步于前文所描述的地方,笔者整个研究生期间或可围绕这个开展一些工作,这部分可以参考一位大佬的作品,他使用verilog手搓了整个TCP/IP协议栈,令人叹为观止,点此前往大佬博客。
  • 这部分内容目前暂时尚未看到完整的代码仓库,当然笔者找到了一篇相关的论文,遗憾的是作者并没有对其进行代码开源。有兴趣的读者可自行了解他们的内容。Calling hardware procedures in a reconfigurable accelerator using RPC-FPGA
  • 笔者深信,随着数据迎来进一步的爆发性增长,程序并行性在未来将变得愈加重要。异构系统在各个领域必将迸发出巨大的生命力,而高性能通信框架和卓越的异构系统,将成为未来片上系统中不可或缺的生命支柱。。

特别鸣谢

文章的最后,深深感谢中科大的孟老师。 孟老师非常善于引导学生的发散思维,让我们进行一些挑战,比如本文的背景就是笔者上课后突发奇想而开始着手调研、实现的。孟老师不仅专业知识深厚,而且涉猎很广,通过这次的网络程序设计课程,我深入到网络协议底层,学会了许多网络协议的本质。特别地,孟老师与其他授课老师不同的是,他的课上往往在潜意识里和学生强调要将个人命运与国家命运深深融合,鼓励我们向更为困难、更有挑战性的事情发起冲锋,他的言辞教诲之中充满着对知识的热爱和对祖国的深情厚谊,为我们树立了崇高的奉献精神典范,在未来的深入学习之中,更要秉持孟老师的敦敦教诲,为祖国做出个人绵薄的贡献。文章来源地址https://www.toymoban.com/news/detail-773369.html

some link

  1. https://developer.aliyun.com/article/1074501
  2. https://doc.oschina.net/grpc?t=58008
  3. https://core.ac.uk/download/pdf/153399229.pdf
  4. https://indico.cern.ch/event/942656/contributions/3960947/attachments/2100003/3530357/irishep_aas_09sep20.pdf
  5. https://ieeexplore.ieee.org/document/8280158
  6. https://www.zhihu.com/question/30027669/answer/1694567359

到了这里,关于gRPC在CPU-FPGA异构系统上的应用与展望的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 机器视觉系统在汽车车轮毂检测上的应用

    将机器视觉用于轮毂检测,可以利用图像分析的方法来测量轮毂特征尺寸、判断轮毂形状,并获取其位置坐标等信息,从而能够辨识流水生产线上的各种款式和型号的汽车轮毂。   市面上对汽车车轮毂具体检测要求如下 : 1.为了分辨流水线上不同类型的轮毂,大约有30种不同

    2024年01月21日
    浏览(46)
  • 知识图谱实战应用17-推荐系统在婚恋交友项目上的实际应用

    大家好,我是微学AI, 今天给大家介绍一下知识图谱实战应用17-推荐系统在婚恋交友项目上的实际应用,推荐系统在婚恋交友项目中的应用可以大大提高用户的匹配效果和满意度。通过利用大数据和智能算法分析用户需求、行为和反馈,推荐系统能够为用户找到更符合他们期

    2024年02月15日
    浏览(75)
  • 轻量应用服务器性能如何?CPU带宽流量系统盘测评

    轻量应用服务器性能如何?腾讯云轻量应用服务器是一种轻量级搭建小型网站和应用的服务器,相对于其他更高性能配置的服务器CVM,性价比更高。虽然其性能不如高性能云服务器CVM,但对于小型网站和应用来说,能够提供基本的计算和存储资源,可以满足基础的业务需求,

    2024年02月10日
    浏览(64)
  • 区块链技术应用展望

    区块链作为一个\\\"去中心化\\\"的分布式账本数据库,能够让数据的产生、运行和应用更加公开与透明。区块链被认为是互联网之后又一大创新之举,是第二个互联网时代——价值互联网时代的来临,区块链将从基础设施层面为各行各业带来巨大的变革机会。 在区块链的研究与应

    2024年02月02日
    浏览(37)
  • 深度学习推荐系统(八)AFM模型及其在Criteo数据集上的应用

    沿着特征工程自动化的思路,深度学习模型从 PNN ⼀路⾛来,经过了Wide&Deep、Deep&Cross、FNN、DeepFM、NFM等模型,进⾏了大量的、基于不同特征互操作思路的尝试。 但特征工程的思路走到这里几乎已经穷尽了可能的尝试,模型进⼀步提升的空间非常小,这也是这类模型的局限

    2024年02月09日
    浏览(52)
  • 深度学习推荐系统(二)Deep Crossing及其在Criteo数据集上的应用

    在2016年, 随着微软的Deep Crossing, 谷歌的WideDeep以及FNN、PNN等一大批优秀的深度学习模型被提出, 推荐系统全面进入了深度学习时代, 时至今日, 依然是主流。 推荐模型主要有下面两个进展: 与传统的机器学习模型相比, 深度学习模型的表达能力更强, 能够挖掘更多数据

    2024年02月10日
    浏览(44)
  • 深度学习推荐系统(三)NeuralCF及其在ml-1m电影数据集上的应用

    在2016年, 随着微软的Deep Crossing, 谷歌的WideDeep以及FNN、PNN等一大批优秀的深度学习模型被提出, 推荐系统全面进入了深度学习时代, 时至今日, 依然是主流。 推荐模型主要有下面两个进展: 与传统的机器学习模型相比, 深度学习模型的表达能力更强, 能够挖掘更多数据

    2024年02月10日
    浏览(60)
  • ChatGPT时代对大数据应用的展望

    目录 一、ChatGPT提供的技术能力分析 1.1ChatGpt是什么? 1.2 实现ChatGpt需要什么? 二、大模型在大数据领域应用分析 2.1 大数据领域应用需求分析 2.2 大数据每个阶段需求人工智能应用的可能 2.2.1 ChatGPT在搭建大数据基础生态的可能性分析 2.2.2 ChatGPT在数据处理周期的可能性分析

    2024年01月22日
    浏览(40)
  • 深度学习推荐系统(五)Deep&Crossing模型及其在Criteo数据集上的应用

    在2016年, 随着微软的Deep Crossing, 谷歌的WideDeep以及FNN、PNN等一大批优秀的深度学习模型被提出, 推荐系统全面进入了深度学习时代, 时至今日, 依然是主流。 推荐模型主要有下面两个进展: 与传统的机器学习模型相比, 深度学习模型的表达能力更强, 能够挖掘更多数据

    2024年02月09日
    浏览(50)
  • 深度学习推荐系统(四)Wide&Deep模型及其在Criteo数据集上的应用

    在2016年, 随着微软的Deep Crossing, 谷歌的WideDeep以及FNN、PNN等一大批优秀的深度学习模型被提出, 推荐系统全面进入了深度学习时代, 时至今日, 依然是主流。 推荐模型主要有下面两个进展: 与传统的机器学习模型相比, 深度学习模型的表达能力更强, 能够挖掘更多数据

    2024年02月09日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包