Rust异步编程实战:构建高性能网络应用

Rust异步编程实战:构建高性能网络应用

Rust异步编程实战:构建高性能网络应用

在这里插入图片描述

一、异步编程概述

1.1 同步vs异步的区别

💡在传统的同步编程中,代码按照顺序执行,每个操作必须等待前一个完成才能继续。例如,发送网络请求时,主线程会阻塞直到响应返回,这种方式简单直观,但在高并发场景下效率低下,因为大量线程会因阻塞而闲置。

异步编程则允许代码在等待操作完成时继续执行其他任务。当一个异步操作开始后,程序会立即返回并继续处理下一个任务,直到该操作完成后通过回调或事件通知继续执行后续代码。这种方式显著提高了CPU利用率和系统的并发处理能力。

1.2 Rust异步编程的演进

Rust的异步编程经历了几个重要阶段:

  • 早期阶段:依赖futures库提供基础的Future和Executor支持,但语法冗长且难以使用。
  • 2018 Edition:引入了async/await语法糖的实验版本,简化了异步代码的编写。
  • 2021 Editionasync/await正式稳定,成为Rust异步编程的标准范式。
  • 生态成熟:Tokio、async-std等异步运行时库的发展,以及大量异步IO库的出现,使Rust在异步编程领域具备了强大的生产能力。

1.3 核心概念:Future、Poll、Waker

在Rust异步编程中,有三个核心概念:

Future(未来)

Future是异步操作的抽象表示,它代表一个尚未完成但最终会产生结果的计算。在Rust中,Future是一个trait,定义如下:

usestd::future::Future;usestd::pin::Pin;usestd::task::{Context,Poll};pubtraitFuture{typeOutput;fnpoll(self:Pin<&mutSelf>, cx:&mutContext)->Poll<Self::Output>;}

💡Output是异步操作的结果类型。poll方法是Future的核心,它尝试推进异步操作的执行。

Poll(轮询)

Pollpoll方法的返回类型,用于表示异步操作的状态:

  • Poll::Ready(value):异步操作已完成,返回结果value
  • Poll::Pending:异步操作尚未完成,需要再次调用poll方法。
Waker(唤醒器)

当Future返回Pending时,它需要一种方式通知Executor(执行器)在操作完成后再次轮询。Waker就是负责这个通知机制的组件。在Context结构体中可以获取到Waker实例,Future可以在需要时保存Waker,以便操作完成后唤醒Executor。

二、async/await语法基础

2.1 async关键字

async关键字用于将代码块或函数标记为异步。异步函数的返回值是一个实现了Future trait的匿名类型。例如:

asyncfnfetch_data()->String{// 模拟网络请求"Hello, async!".to_string()}fnmain(){let future =fetch_data();// 这里并没有执行异步代码println!("Future created");}

💡调用异步函数只会创建一个Future对象,不会立即执行代码。我们需要一个Executor来驱动Future的执行。

2.2 await关键字

await关键字用于暂停异步代码的执行,直到Future完成。await必须在async函数或async代码块中使用。例如:

asyncfnfetch_data()->String{"Hello, async!".to_string()}asyncfnprocess(){println!("Start processing");let data =fetch_data().await;// 暂停执行,等待fetch_data完成println!("Data received: {}", data);}use tokio;#[tokio::main]asyncfnmain(){process().await;// 驱动process()的执行}

⚠️注意:await关键字会自动处理Future的状态和唤醒机制,让异步代码看起来像同步代码一样。

2.3 async函数的返回类型

异步函数的返回类型是一个实现了Future trait的匿名类型。如果需要显式指定返回类型,可以使用Box<dyn Future<Output = T>>。例如:

usestd::future::Future;fnfetch_data()->implFuture<Output=String>{asyncmove{"Hello, async!".to_string()}}#[tokio::main]asyncfnmain(){let data =fetch_data().await;println!("{}", data);}

2.4 异步代码的执行

Rust的异步代码需要一个Executor来执行。Executor负责管理任务的调度、唤醒和执行。常用的Executor有Tokio和async-std。例如,使用Tokio的#[tokio::main]宏可以轻松启动一个异步执行器:

use tokio;#[tokio::main]asyncfnmain(){println!("Hello from async main");// 异步代码}

三、Tokio异步运行时

3.1 Tokio的架构与核心组件

Tokio是Rust最流行的异步运行时库,提供了高性能的网络IO、定时器、任务调度等功能。它的架构主要由以下组件构成:

  • Scheduler(调度器):负责管理异步任务的执行和调度。
  • Reactor(反应堆):负责处理IO事件,如网络连接、文件操作等。
  • Runtime(运行时):整合Scheduler和Reactor,提供统一的接口供用户使用。

3.2 安装与配置Tokio

在Cargo.toml中添加Tokio依赖:

[dependencies] tokio = { version = "1.0", features = ["full"] } 

💡features = ["full"]会启用Tokio的所有功能。如果需要更小的二进制文件,可以只启用所需的功能,如["rt-multi-thread", "macros"]

3.3 基本使用示例:TCP服务器/客户端

TCP服务器
usetokio::net::TcpListener;usetokio::io::{AsyncReadExt,AsyncWriteExt};#[tokio::main]asyncfnmain(){let listener =TcpListener::bind("127.0.0.1:8080").await.unwrap();println!("Server listening on 127.0.0.1:8080");loop{let(mut socket, addr)= listener.accept().await.unwrap();println!("Accepted connection from {}", addr);tokio::spawn(asyncmove{letmut buf =[0;1024];loop{match socket.read(&mut buf).await{Ok(0)=>{println!("Client {} disconnected", addr);return;}Ok(n)=>{let data =&buf[..n];println!("Received from {}: {}", addr,String::from_utf8_lossy(data)); socket.write_all(data).await.unwrap();}Err(e)=>{println!("Error reading from client {}: {}", addr, e);return;}}}});}}
TCP客户端
usetokio::net::TcpStream;usetokio::io::{AsyncReadExt,AsyncWriteExt};#[tokio::main]asyncfnmain(){letmut stream =TcpStream::connect("127.0.0.1:8080").await.unwrap();println!("Connected to server");let message ="Hello, Tokio!"; stream.write_all(message.as_bytes()).await.unwrap();println!("Sent: {}", message);letmut buf =[0;1024];let n = stream.read(&mut buf).await.unwrap();let response =String::from_utf8_lossy(&buf[..n]);println!("Received: {}", response);}

3.4 Tokio的任务调度与管理

Tokio提供了任务调度功能,可以创建轻量级的异步任务。任务分为两种类型:

  • 根任务:通过#[tokio::main]宏创建的主任务。
  • 子任务:通过tokio::spawn函数创建的任务。

任务调度器会自动管理任务的执行,确保CPU资源的高效利用。例如:

use tokio;usestd::time::Duration;#[tokio::main]asyncfnmain(){// 创建三个子任务let task1 =tokio::spawn(async{tokio::time::sleep(Duration::from_secs(1)).await;println!("Task 1 completed");});let task2 =tokio::spawn(async{tokio::time::sleep(Duration::from_secs(2)).await;println!("Task 2 completed");});let task3 =tokio::spawn(async{tokio::time::sleep(Duration::from_secs(3)).await;println!("Task 3 completed");});// 等待所有任务完成 task1.await.unwrap(); task2.await.unwrap(); task3.await.unwrap();println!("All tasks completed");}

四、异步IO操作

4.1 异步文件IO

Tokio提供了异步文件IO的支持。例如,读取文件内容:

usetokio::fs::File;usetokio::io::AsyncReadExt;#[tokio::main]asyncfnmain(){letmut file =File::open("test.txt").await.unwrap();letmut content =String::new(); file.read_to_string(&mut content).await.unwrap();println!("File content: {}", content);}

4.2 异步网络IO

除了TCP,Tokio还支持UDP、Unix域套接字等网络协议。例如,UDP服务器:

usetokio::net::UdpSocket;#[tokio::main]asyncfnmain(){let socket =UdpSocket::bind("127.0.0.1:8080").await.unwrap();println!("UDP server listening on 127.0.0.1:8080");letmut buf =[0;1024];loop{let(n, addr)= socket.recv_from(&mut buf).await.unwrap();println!("Received from {}: {}", addr,String::from_utf8_lossy(&buf[..n])); socket.send_to(&buf[..n], addr).await.unwrap();}}

4.3 异步Redis操作(使用redis-rs库)

redis-rs库提供了异步Redis操作的支持。在Cargo.toml中添加依赖:

[dependencies] redis = { version = "0.22", features = ["tokio-comp"] } 

示例代码:

useredis::AsyncCommands;#[tokio::main]asyncfnmain(){let client =redis::Client::open("redis://127.0.0.1/").unwrap();letmut conn = client.get_async_connection().await.unwrap();// 设置键值对let _:()= conn.set("key","value").await.unwrap();println!("Set key:value");// 获取键值对let value:String= conn.get("key").await.unwrap();println!("Get key: {}", value);// 删除键值对let _:()= conn.del("key").await.unwrap();println!("Del key");}

4.4 异步HTTP请求(使用reqwest库)

reqwest是Rust常用的HTTP客户端库,支持异步操作。在Cargo.toml中添加依赖:

[dependencies] reqwest = "0.11" tokio = { version = "1.0", features = ["full"] } 

示例代码:

use reqwest;#[tokio::main]asyncfnmain(){let url ="https://httpbin.org/get";let response =reqwest::get(url).await.unwrap();println!("Status: {}", response.status());let body = response.text().await.unwrap();println!("Body: {}", body);}

五、异步编程的高级话题

5.1 Pin与Unpin

在异步编程中,Future可能包含自引用结构体,即结构体的某个字段指向自身的另一个字段。这种情况下,Future的内存地址必须固定,否则当Future被移动时,引用会失效。Pin trait就是用来确保Future不会被移动的。

Unpin trait表示Future可以安全地移动。大多数类型默认实现了Unpin,只有包含自引用的类型需要显式实现Pin。例如:

usestd::future::Future;usestd::pin::Pin;usestd::task::{Context,Poll};structMyFuture{ done:bool, data:Option<String>,}implFutureforMyFuture{typeOutput=String;fnpoll(mutself:Pin<&mutSelf>, cx:&mutContext)->Poll<Self::Output>{ifself.done {Poll::Ready(self.data.take().unwrap())}else{self.done =true;self.data =Some("Data from MyFuture".to_string()); cx.waker().wake_by_ref();// 唤醒自己Poll::Pending}}}#[tokio::main]asyncfnmain(){let future =MyFuture{ done:false, data:None};let result = future.await;println!("{}", result);}

5.2 组合Future:join、select、try_join

Future的组合操作可以让我们并发执行多个异步任务,并对结果进行处理。Tokio提供了多种组合器:

join:等待所有任务完成
usetokio::join;usestd::time::Duration;asyncfntask1()->String{tokio::time::sleep(Duration::from_secs(1)).await;"Task 1 completed".to_string()}asyncfntask2()->String{tokio::time::sleep(Duration::from_secs(2)).await;"Task 2 completed".to_string()}#[tokio::main]asyncfnmain(){let(res1, res2)=join!(task1(),task2());println!("{}", res1);println!("{}", res2);}
select:等待第一个任务完成
usetokio::select;usestd::time::Duration;asyncfntask1()->String{tokio::time::sleep(Duration::from_secs(1)).await;"Task 1 completed".to_string()}asyncfntask2()->String{tokio::time::sleep(Duration::from_secs(2)).await;"Task 2 completed".to_string()}#[tokio::main]asyncfnmain(){select!{ res1 =task1()=>println!("{}", res1), res2 =task2()=>println!("{}", res2),}}
try_join:处理返回Result的任务
usetokio::try_join;usestd::time::Duration;asyncfntask1()->Result<String,String>{tokio::time::sleep(Duration::from_secs(1)).await;Ok("Task 1 completed".to_string())}asyncfntask2()->Result<String,String>{tokio::time::sleep(Duration::from_secs(2)).await;Err("Task 2 failed".to_string())}#[tokio::main]asyncfnmain(){matchtry_join!(task1(),task2()){Ok((res1, res2))=>{println!("{}", res1);println!("{}", res2);}Err(e)=>println!("Error: {}", e),}}

5.3 超时与取消

在异步编程中,我们经常需要处理任务超时和取消的情况。Tokio提供了相应的API:

超时
usetokio::time::timeout;usestd::time::Duration;asyncfnlong_running_task()->String{tokio::time::sleep(Duration::from_secs(3)).await;"Task completed".to_string()}#[tokio::main]asyncfnmain(){matchtimeout(Duration::from_secs(2),long_running_task()).await{Ok(res)=>println!("{}", res),Err(e)=>println!("Timeout: {}", e),}}
取消
usetokio::select;usestd::time::Duration;asyncfntask()->String{loop{println!("Task running...");tokio::time::sleep(Duration::from_secs(1)).await;}}#[tokio::main]asyncfnmain(){letmut interval =tokio::time::interval(Duration::from_secs(2));select!{ res =task()=>println!("{}", res), _ = interval.tick()=>println!("Task cancelled"),}}

5.4 错误处理:Result与async/await的结合

异步函数可以返回Result类型,结合?运算符进行错误处理:

use reqwest;usestd::io;asyncfnfetch_data(url:&str)->Result<String,Box<dynstd::error::Error>>{let response =reqwest::get(url).await?;let body = response.text().await?;Ok(body)}asyncfnwrite_to_file(content:&str, filename:&str)->Result<(),Box<dynstd::error::Error>>{letmut file =tokio::fs::File::create(filename).await?;tokio::io::AsyncWriteExt::write_all(&mut file, content.as_bytes()).await?;Ok(())}#[tokio::main]asyncfnmain()->Result<(),Box<dynstd::error::Error>>{let url ="https://httpbin.org/get";let content =fetch_data(url).await?;write_to_file(&content,"data.txt").await?;println!("Data saved to data.txt");Ok(())}

六、实战项目:高性能HTTP API服务

6.1 项目需求与架构设计

我们将构建一个高性能的HTTP API服务,提供用户的CRUD操作。项目需求如下:

  • 支持用户的创建、读取、更新、删除操作
  • 数据存储在PostgreSQL数据库中
  • API响应格式为JSON
  • 支持跨域请求
  • 高性能的异步处理

项目架构设计:

  • 使用Axum作为HTTP框架(轻量级、高性能)
  • 使用Tokio作为异步运行时
  • 使用SQLx作为数据库访问层(异步、类型安全)
  • 使用CORS中间件处理跨域请求
  • 使用Logger中间件记录请求日志

6.2 依赖配置与项目初始化

创建项目:

cargo new rust-async-api cd rust-async-api 

在Cargo.toml中添加依赖:

[dependencies] axum = "0.5" tokio = { version = "1.0", features = ["full"] } sqlx = { version = "0.6", features = ["postgres", "runtime-tokio-rustls"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tower-http = { version = "0.3", features = ["cors", "trace"] } 

初始化PostgreSQL数据库:

CREATEDATABASE rust_async_api;CREATETABLE users ( id SERIALPRIMARYKEY, name VARCHAR(100)NOTNULL, email VARCHAR(100)NOTNULLUNIQUE, created_at TIMESTAMPDEFAULTCURRENT_TIMESTAMP);

6.3 实现API端点

数据库连接

创建src/db.rs:

usesqlx::PgPool;pubasyncfncreate_pool(database_url:&str)->PgPool{PgPool::connect(database_url).await.unwrap()}
用户数据模型

创建src/models.rs:

useserde::{Deserialize,Serialize};usesqlx::FromRow;#[derive(Debug, FromRow, Serialize, Deserialize)]pubstructUser{pub id:i32,pub name:String,pub email:String,pub created_at:chrono::DateTime<chrono::Utc>,}#[derive(Debug, Deserialize)]pubstructCreateUser{pub name:String,pub email:String,}#[derive(Debug, Deserialize)]pubstructUpdateUser{pub name:Option<String>,pub email:Option<String>,}
API路由处理

创建src/handlers.rs:

useaxum::{extract::{Path,State},http::StatusCode,response::IntoResponse,Json,};usesqlx::PgPool;usecrate::models::{CreateUser,UpdateUser,User};pubasyncfnget_users(State(pool):State<PgPool>)->implIntoResponse{let users =sqlx::query_as!(User,"SELECT * FROM users").fetch_all(&pool).await;match users {Ok(users)=>(StatusCode::OK,Json(users)).into_response(),Err(_)=>(StatusCode::INTERNAL_SERVER_ERROR,"Failed to fetch users").into_response(),}}pubasyncfnget_user(Path(user_id):Path<i32>,State(pool):State<PgPool>,)->implIntoResponse{let user =sqlx::query_as!(User,"SELECT * FROM users WHERE id = $1", user_id).fetch_one(&pool).await;match user {Ok(user)=>(StatusCode::OK,Json(user)).into_response(),Err(sqlx::Error::RowNotFound)=>(StatusCode::NOT_FOUND,"User not found").into_response(),Err(_)=>(StatusCode::INTERNAL_SERVER_ERROR,"Failed to fetch user").into_response(),}}pubasyncfncreate_user(State(pool):State<PgPool>,Json(payload):Json<CreateUser>,)->implIntoResponse{let user =sqlx::query_as!(User,"INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *", payload.name, payload.email ).fetch_one(&pool).await;match user {Ok(user)=>(StatusCode::CREATED,Json(user)).into_response(),Err(sqlx::Error::Database(dbe))if dbe.code().unwrap_or("").eq_ignore_ascii_case("23505")=>{(StatusCode::BAD_REQUEST,"Email already exists").into_response()}Err(_)=>(StatusCode::INTERNAL_SERVER_ERROR,"Failed to create user").into_response(),}}pubasyncfnupdate_user(Path(user_id):Path<i32>,State(pool):State<PgPool>,Json(payload):Json<UpdateUser>,)->implIntoResponse{let user =sqlx::query_as!(User,"SELECT * FROM users WHERE id = $1", user_id).fetch_one(&pool).await;if user.is_err(){return(StatusCode::NOT_FOUND,"User not found").into_response();}let user = user.unwrap();let name = payload.name.unwrap_or(user.name);let email = payload.email.unwrap_or(user.email);let updated_user =sqlx::query_as!(User,"UPDATE users SET name = $1, email = $2 WHERE id = $3 RETURNING *", name, email, user_id ).fetch_one(&pool).await;match updated_user {Ok(user)=>(StatusCode::OK,Json(user)).into_response(),Err(sqlx::Error::Database(dbe))if dbe.code().unwrap_or("").eq_ignore_ascii_case("23505")=>{(StatusCode::BAD_REQUEST,"Email already exists").into_response()}Err(_)=>(StatusCode::INTERNAL_SERVER_ERROR,"Failed to update user").into_response(),}}pubasyncfndelete_user(Path(user_id):Path<i32>,State(pool):State<PgPool>,)->implIntoResponse{let result =sqlx::query!("DELETE FROM users WHERE id = $1", user_id).execute(&pool).await;match result {Ok(result)if result.rows_affected()==0=>(StatusCode::NOT_FOUND,"User not found").into_response(),Ok(_)=>(StatusCode::NO_CONTENT,"").into_response(),Err(_)=>(StatusCode::INTERNAL_SERVER_ERROR,"Failed to delete user").into_response(),}}
应用程序入口

创建src/main.rs:

useaxum::{routing::{get, post, put, delete},Router,};usetower_http::{cors::CorsLayer,trace::TraceLayer};usestd::env;usecrate::db::create_pool;usecrate::handlers::{get_users, get_user, create_user, update_user, delete_user};moddb;modhandlers;modmodels;#[tokio::main]asyncfnmain(){// 初始化日志tracing_subscriber::fmt::init();// 从环境变量获取数据库URLlet database_url =env::var("DATABASE_URL").expect("DATABASE_URL is required");let pool =create_pool(&database_url).await;// 配置CORSlet cors =CorsLayer::permissive();// 创建路由let app =Router::new().route("/users",get(get_users)).route("/users",post(create_user)).route("/users/:id",get(get_user)).route("/users/:id",put(update_user)).route("/users/:id",delete(delete_user)).layer(cors).layer(TraceLayer::new_for_http()).with_state(pool);// 启动服务器let listener =tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();println!("Server running on http://0.0.0.0:3000");axum::serve(listener, app).await.unwrap();}

6.4 性能优化与测试

性能优化
  • 连接池:使用SQLx的PgPool管理数据库连接,避免频繁创建和销毁连接。
  • 异步IO:使用Axum和Tokio的异步特性,提高请求处理的并发能力。
  • 查询优化:使用索引优化数据库查询,例如在email字段上创建唯一索引。
性能测试

使用wrk进行性能测试:

wrk -t12 -c400 -d30s http://localhost:3000/users 

测试结果(示例):

Running 30s test @ http://localhost:3000/users 12 threads and 400 connections Thread Stats Avg Stdev Max +/- Stdev Latency 50.19ms 20.12ms 200.00ms 68.00% Req/Sec 666.67 80.00 800.00 80.00% 239999 requests in 30.01s, 50.00MB read Requests/sec: 7997.00 Transfer/sec: 1.67MB 

6.5 部署与监控

部署

可以使用Docker容器化部署API服务。创建Dockerfile:

FROM rust:1.60 as builder WORKDIR /app COPY Cargo.toml Cargo.lock ./ RUN cargo new --bin rust-async-api WORKDIR /app/rust-async-api COPY src ./src RUN cargo build --release FROM debian:buster-slim RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/* COPY --from=builder /app/rust-async-api/target/release/rust-async-api /usr/local/bin/ EXPOSE 3000 CMD ["rust-async-api"] 

构建并运行Docker容器:

docker build -t rust-async-api .docker run -p 3000:3000 -e DATABASE_URL=postgresql://user:password@host:5432/rust_async_api rust-async-api 
监控

使用Prometheus和Grafana监控API服务的性能。添加Prometheus依赖:

[dependencies] axum-prometheus = "0.4" prometheus = "0.13" 

在src/main.rs中添加Prometheus监控:

useaxum_prometheus::PrometheusMetrics;let prometheus =PrometheusMetrics::new("api",Some("/metrics"));let app =Router::new()// 其他路由.route("/metrics",get(prometheus.metrics)).layer(prometheus.layer())// 其他中间件.with_state(pool);

七、常见问题与最佳实践

7.1 异步编程中的常见错误

  • 阻塞操作:在异步任务中使用同步IO或长时间运行的计算,会阻塞任务调度器,影响性能。解决方案是使用异步版本的API或在单独的线程中执行阻塞操作。
  • 任务泄漏:创建的任务未被正确取消,导致资源泄漏。解决方案是使用Tokio的cancel机制或监控任务的状态。
  • 死锁:多个任务之间相互等待对方完成,导致死锁。解决方案是避免循环依赖或使用超时机制。

7.2 性能优化技巧

  • 最小化任务创建:避免在循环中频繁创建任务,尽可能复用任务。
  • 合理设置线程数:根据CPU核心数调整Tokio的工作线程数。
  • 使用连接池:对数据库、Redis等资源使用连接池,避免频繁创建和销毁连接。
  • 避免不必要的await:在不需要等待的地方避免使用await,提高代码的执行效率。

7.3 代码风格与可读性

  • 使用async/await语法:优先使用async/await语法糖,避免手写Future实现。
  • 模块化设计:将异步代码分为多个模块,提高代码的可读性和可维护性。
  • 错误处理:使用Result类型统一处理错误,并提供有意义的错误信息。
  • 文档注释:为异步函数和方法添加详细的文档注释,说明其功能和使用方法。

7.4 异步生态推荐

  • 异步运行时:Tokio、async-std
  • HTTP框架:Axum、Actix-web、Rocket
  • 数据库访问:SQLx、Diesel(支持异步)
  • Redis操作:redis-rs
  • HTTP客户端:reqwest
  • 异步文件IO:Tokio fs、async-std fs
  • 监控工具:Prometheus、Grafana、Tracing

八、总结

Rust的异步编程提供了高性能、内存安全的并发处理能力。通过async/await语法,我们可以编写出与同步代码类似的异步代码,同时获得异步编程的优势。Tokio作为成熟的异步运行时库,提供了丰富的API和组件,使开发高性能网络应用变得更加简单。

在实际项目中,我们需要注意异步编程的常见错误,遵循最佳实践,同时利用生态系统中的工具和库来提高开发效率和代码质量。希望本章的内容能够帮助您快速掌握Rust异步编程的核心技术,并在实际项目中应用。

Read more

【人工智能】多目标融合算法(一):样本Loss加权(Sample Loss Reweight)

【人工智能】多目标融合算法(一):样本Loss加权(Sample Loss Reweight)

目录 一、引言 二、样本Loss提权 2.1 技术原理 2.2 技术优缺点 2.3 代码实例 三、总结 一、引言 在朴素的深度学习ctr预估模型中(如DNN),通常以一个行为为预估目标,比如通过ctr预估点击率。但实际推荐系统业务场景中,更多是多种目标融合的结果,比如视频推荐,会存在视频点击率、视频完整播放率、视频播放时长等多个目标,而多种目标如何更好的融合,在工业界与学术界均有较多内容产出,由于该环节对实际业务影响最为直接,特开此专栏对推荐系统深度学习多目标问题进行讲述。 今天重点介绍“样本Loss提权”,该方法通过训练时Loss乘以样本权重实现对其它目标的加权,方法最为简单。 二、样本Loss提权 2.1 技术原理 所有目标使用一个模型,在标注正样本时,考虑多个目标。例如对于点击和播放,在标注正样本时,给予不同的权重,使它们综合体现在模型目标中。如下表,以视频业务为例,

By Ne0inhk
Spring AI Agent 模式:为什么你的AI Agent总是忘事

Spring AI Agent 模式:为什么你的AI Agent总是忘事

研究表明,大语言模型存在一个被称为"Lost in the Middle"的问题——当上下文变长时,模型对中间位置的信息注意力会显著下降。开头和结尾的内容记得清清楚楚,中间的任务就容易被"遗忘"。当你的Agent需要同时处理文件编辑、测试执行、文档更新等多个步骤时,某些重要步骤就可能悄无声息地消失了。 你有没有遇到过这种情况:让AI Agent执行一个复杂的多步骤任务,结果它做到一半就悄悄跳过了某个关键步骤?比如你让它修改代码、运行测试、更新文档,最后发现测试根本没跑。 这不是个例。 研究表明,大语言模型存在一个被称为"Lost in the Middle"的问题——当上下文变长时,模型对中间位置的信息注意力会显著下降。 开头和结尾的内容记得清清楚楚,中间的任务就容易被"遗忘"。当你的Agent需要同时处理文件编辑、测试执行、文档更新等多个步骤时,某些重要步骤就可能悄无声息地消失了。 有没有什么好办法?

By Ne0inhk
告别“选择困难症”:我是如何用 AI Ping 实现大模型自由,还能省下 50% 成本的?

告别“选择困难症”:我是如何用 AI Ping 实现大模型自由,还能省下 50% 成本的?

告别“选择困难症”:我是如何用 AI Ping 实现大模型自由,还能省下 50% 成本的? * 写在最前面 * 场景一:从“写脚本卡壳”到“批量生成” * 场景二:开发路上的“万能插头” * 使用感受 * 一点小建议与期待 * 写在最后 🌈你好呀!我是 是Yu欸🚀 感谢你的陪伴与支持~ 欢迎添加文末好友🌌 在所有感兴趣的领域扩展知识,不定期掉落福利资讯(*^▽^*) 写在最前面 版权声明:本文为原创,遵循 CC 4.0 BY-SA 协议。转载请注明出处。 在这个大模型“百花齐放”甚至“百模大战”的时代,作为一名既要写代码开发,又要频繁输出技术内容(写博文、做视频)的开发者,我每天最大的烦恼就是: “今天这个任务,

By Ne0inhk

ESP32 小智 AI 机器人入门教程从原理到实现(自己云端部署)

此博客为一篇针对初学者的详细教程,涵盖小智 AI 机器人的原理、硬件准备、软件环境搭建、代码实现、云端部署以及优化扩展。文章结合了现有的网络资源,取长补短,确保内容易于理解和操作。 简介: 本教程将指导初学者使用 ESP32 微控制器开发一个简单的语音对话机器人“小智”。我们将介绍所需的基础原理、硬件准备、软件环境搭建,以及如何编写代码实现语音唤醒和与云端大模型的对接。通过本教程,即使没有深厚的 AI 或嵌入式经验,也可以一步步制作出一个能听懂唤醒词并与人对话的简易 AI 机器人。本教程提供详细的操作步骤、代码示例和图示,帮助您轻松上手。 1. 基础原理 ESP32 架构及其在 AI 领域的应用: ESP32 是一款集成 Wi-Fi 和蓝牙的双核微控制器,具有较高的主频和丰富的外设接口,适合物联网和嵌入式 AI 应用。特别是新版的 ESP32-S3 芯片,不仅运行频率高达 240MHz,还内置了向量加速指令(

By Ne0inhk