首页
关于
Search
1
微服务
11 阅读
2
图神经网络
11 阅读
3
微信小程序
10 阅读
4
欢迎使用 Typecho
9 阅读
5
数学基础
8 阅读
默认分类
科研
自学
登录
找到
54
篇与
zy123
相关的结果
- 第 2 页
2025-05-21
Mybatis&-Plus
Mybatis 快速创建 创建springboot工程(Spring Initializr),并导入 mybatis的起步依赖、mysql的驱动包。创建用户表user,并创建对应的实体类User 在springboot项目中,可以编写main/resources/application.properties文件,配置数据库连接信息。 #驱动类名称 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver #数据库连接的url spring.datasource.url=jdbc:mysql://localhost:3306/mybatis #连接数据库的用户名 spring.datasource.username=root #连接数据库的密码 spring.datasource.password=1234 在引导类所在包下,在创建一个包 mapper。在mapper包下创建一个接口 UserMapper @Mapper注解:表示是mybatis中的Mapper接口 -程序运行时:框架会自动生成接口的实现类对象(代理对象),并交给Spring的IOC容器管理 @Select注解:代表的就是select查询,用于书写select查询语句 @Mapper public interface UserMapper { //查询所有用户数据 @Select("select * from user") public List<User> list(); } 数据库连接池 数据库连接池是一个容器,负责管理和分配数据库连接(Connection)。 在程序启动时,连接池会创建一定数量的数据库连接。 客户端在执行 SQL 时,从连接池获取连接对象,执行完 SQL 后,将连接归还给连接池,以供其他客户端复用。 如果连接对象长时间空闲且超过预设的最大空闲时间,连接池会自动释放该连接。 优势:避免频繁创建和销毁连接,提高数据库访问效率。 Druid(德鲁伊) Druid连接池是阿里巴巴开源的数据库连接池项目 功能强大,性能优秀,是Java语言最好的数据库连接池之一 把默认的 Hikari 数据库连接池切换为 Druid 数据库连接池: 在pom.xml文件中引入依赖 <dependency> <!-- Druid连接池依赖 --> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.8</version> </dependency> 在application.properties中引入数据库连接配置 spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.druid.url=jdbc:mysql://localhost:3306/mybatis spring.datasource.druid.username=root spring.datasource.druid.password=123456 SQL注入问题 SQL注入:由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。 在Mybatis中提供的参数占位符有两种:${...} 、#{...} #{...} 执行SQL时,会将#{…}替换为?,生成预编译SQL,会自动设置参数值 使用时机:参数传递,都使用#{…} ${...} 拼接SQL。直接将参数拼接在SQL语句中,存在SQL注入问题 使用时机:如果对表名、列表进行动态设置时使用 日志输出 只建议开发环境使用:在Mybatis当中我们可以借助日志,查看到sql语句的执行、执行传递的参数以及执行结果 打开application.properties文件 开启mybatis的日志,并指定输出到控制台 #指定mybatis输出日志的位置, 输出控制台 mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl 驼峰命名法 在 Java 项目中,数据库表字段名一般使用 下划线命名法(snake_case),而 Java 中的变量名使用 驼峰命名法(camelCase)。 小驼峰命名(lowerCamelCase): 第一个单词的首字母小写,后续单词的首字母大写。 例子:firstName, userName, myVariable 大驼峰命名(UpperCamelCase): 每个单词的首字母都大写,通常用于类名或类型名。 例子:MyClass, EmployeeData, OrderDetails 表中查询的数据封装到实体类中 实体类属性名和数据库表查询返回的字段名一致,mybatis会自动封装。 如果实体类属性名和数据库表查询返回的字段名不一致,不能自动封装。 解决方法: 起别名 结果映射 开启驼峰命名 属性名和表中字段名保持一致 开启驼峰命名(推荐):如果字段名与属性名符合驼峰命名规则,mybatis会自动通过驼峰命名规则映射 驼峰命名规则: abc_xyz => abcXyz 表中字段名:abc_xyz 类中属性名:abcXyz 推荐的完整配置: mybatis: #mapper配置文件 mapper-locations: classpath:mapper/*.xml type-aliases-package: com.sky.entity configuration: #开启驼峰命名 map-underscore-to-camel-case: true type-aliases-package: com.sky.entity把 com.sky.entity 包下的所有类都当作别名注册,XML 里就可以直接写 <resultType="Dish"> 而不用写全限定名。可以多添加几个包,用逗号隔开。 增删改 增删改通用!:返回值为int时,表示影响的记录数,一般不需要可以设置为void! 作用于单个字段 @Mapper public interface EmpMapper { //SQL语句中的id值不能写成固定数值,需要变为动态的数值 //解决方案:在delete方法中添加一个参数(用户id),将方法中的参数,传给SQL语句 /** * 根据id删除数据 * @param id 用户id */ @Delete("delete from emp where id = #{id}")//使用#{key}方式获取方法中的参数值 public void delete(Integer id); } 上图参数值分离,有效防止SQL注入 作用于多个字段 @Mapper public interface EmpMapper { //会自动将生成的主键值,赋值给emp对象的id属性 @Options(useGeneratedKeys = true,keyProperty = "id") @Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values (#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})") public void insert(Emp emp); } 在 @Insert 注解中使用 #{} 来引用 Emp 对象的属性,MyBatis 会自动从 Emp 对象中提取相应的字段并绑定到 SQL 语句中的占位符。 @Options(useGeneratedKeys = true, keyProperty = "id") 这行配置表示,插入时自动生成的主键会赋值给 Emp 对象的 id 属性。 // 调用 mapper 执行插入操作 empMapper.insert(emp); // 现在 emp 对象的 id 属性会被自动设置为数据库生成的主键值 System.out.println("Generated ID: " + emp.getId()); 查 查询案例: 姓名:要求支持模糊匹配 性别:要求精确匹配 入职时间:要求进行范围查询 根据最后修改时间进行降序排序 重点在于模糊查询时where name like '%#{name}%' 会报错。 解决方案: 使用MySQL提供的字符串拼接函数:concat('%' , '关键字' , '%') CONCAT() 如果其中任何一个参数为 NULL,CONCAT() 返回 NULL,Like NULL会导致查询不到任何结果! NULL和''是完全不同的 @Mapper public interface EmpMapper { @Select("select * from emp " + "where name like concat('%',#{name},'%') " + "and gender = #{gender} " + "and entrydate between #{begin} and #{end} " + "order by update_time desc") public List<Emp> list(String name, Short gender, LocalDate begin, LocalDate end); } XML配置文件规范 使用Mybatis的注解方式,主要是来完成一些简单的增删改查功能。如果需要实现复杂的SQL功能,建议使用XML来配置映射语句,也就是将SQL语句写在XML配置文件中。 在Mybatis中使用XML映射文件方式开发,需要符合一定的规范: XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名) XML映射文件的namespace属性为Mapper接口全限定名一致 XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致。 <select>标签:就是用于编写select查询语句的。 resultType属性,指的是查询返回的单条记录所封装的类型(查询必须)。 parameterType属性(可选,MyBatis 会根据接口方法的入参类型(比如 Dish 或 DishPageQueryDTO)自动推断),POJO作为入参,需要使用全类名或是type‑aliases‑package: com.sky.entity 下注册的别名。 <insert id="insert" useGeneratedKeys="true" keyProperty="id"> <select id="pageQuery" resultType="com.sky.vo.DishVO"> <select id="list" resultType="com.sky.entity.Dish" parameterType="com.sky.entity.Dish"> 实现过程: resources下创与java下一样的包,即edu/whut/mapper,新建xx.xml文件 配置Mapper文件 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="edu.whut.mapper.EmpMapper"> <!-- SQL 查询语句写在这里 --> </mapper> namespace 属性指定了 Mapper 接口的全限定名(即包名 + 类名)。 编写查询语句 <select id="list" resultType="edu.whut.pojo.Emp"> select * from emp where name like concat('%',#{name},'%') and gender = #{gender} and entrydate between #{begin} and #{end} order by update_time desc </select> id="list":指定查询方法的名称,应该与 Mapper 接口中的方法名称一致。 resultType="edu.whut.pojo.Emp":resultType 只在 查询操作 中需要指定。指定查询结果映射的对象类型,这里是 Emp 类。 这里有bug!!! concat('%',#{name},'%')这里应该用<where> <if>标签对name是否为NULL或''进行判断 动态SQL SQL-if,where <if>:用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接SQL。 <if test="条件表达式"> 要拼接的sql语句 </if> <where>只会在子元素有内容的情况下才插入where子句,而且会自动去除子句的开头的AND或OR,加了总比不加好 <select id="list" resultType="com.itheima.pojo.Emp"> select * from emp <where> <!-- if做为where标签的子元素 --> <if test="name != null"> and name like concat('%',#{name},'%') </if> <if test="gender != null"> and gender = #{gender} </if> <if test="begin != null and end != null"> and entrydate between #{begin} and #{end} </if> </where> order by update_time desc </select> SQL-foreach Mapper 接口 @Mapper public interface EmpMapper { //批量删除 public void deleteByIds(List<Integer> ids); } XML 映射文件 <foreach> 标签用于遍历集合,常用于动态生成 SQL 语句中的 IN 子句、批量插入、批量更新等操作。 <foreach collection="集合名称" item="集合遍历出来的元素/项" separator="每一次遍历使用的分隔符" open="遍历开始前拼接的片段" close="遍历结束后拼接的片段"> </foreach> open="(":这个属性表示,在生成的 SQL 语句开始时添加一个 左括号 (。 close=")":这个属性表示,在生成的 SQL 语句结束时添加一个 右括号 )。 例:批量删除实现 <delete id="deleteByIds"> DELETE FROM emp WHERE id IN <foreach collection="ids" item="id" separator="," open="(" close=")"> #{id} </foreach> </delete> 实现效果类似:DELETE FROM emp WHERE id IN (1, 2, 3); Mybatis-Plus MyBatis-Plus 的使命就是——在保留 MyBatis 灵活性的同时,大幅减少模板化、重复的代码编写,让增删改查、分页等常见场景“开箱即用”,以更少的配置、更少的样板文件、更高的开发效率,帮助团队快速交付高质量的数据库访问层。 快速开始 1.引入依赖 <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <!-- <dependency>--> <!-- <groupId>org.mybatis.spring.boot</groupId>--> <!-- <artifactId>mybatis-spring-boot-starter</artifactId>--> <!-- <version>2.3.1</version>--> <!-- </dependency>--> 由于这个starter包含对mybatis的自动装配,因此完全可以替换掉Mybatis的starter。 2.定义mapper 为了简化单表CRUD,MybatisPlus提供了一个基础的BaseMapper接口,其中已经实现了单表的CRUD(增删查改): 仅需让自定义的UserMapper接口,继承BaseMapper接口: public interface UserMapper extends BaseMapper<User> { } 测试: @SpringBootTest class UserMapperTest { @Autowired private UserMapper userMapper; @Test void testInsert() { User user = new User(); user.setId(5L); user.setUsername("Lucy"); user.setPassword("123"); user.setPhone("18688990011"); user.setBalance(200); user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}"); user.setCreateTime(LocalDateTime.now()); user.setUpdateTime(LocalDateTime.now()); userMapper.insert(user); } @Test void testSelectById() { User user = userMapper.selectById(5L); System.out.println("user = " + user); } @Test void testSelectByIds() { List<User> users = userMapper.selectBatchIds(List.of(1L, 2L, 3L, 4L, 5L)); users.forEach(System.out::println); } @Test void testUpdateById() { User user = new User(); user.setId(5L); user.setBalance(20000); userMapper.updateById(user); } @Test void testDelete() { userMapper.deleteById(5L); } } 3.常见注解 MybatisPlus如何知道我们要查询的是哪张表?表中有哪些字段呢? 约定大于配置 泛型中的User就是与数据库对应的PO. MybatisPlus就是根据PO实体的信息来推断出表的信息,从而生成SQL的。默认情况下: MybatisPlus会把PO实体的类名驼峰转下划线作为表名 UserRecord->user_record MybatisPlus会把PO实体的所有变量名驼峰转下划线作为表的字段名,并根据变量类型推断字段类型 MybatisPlus会把名为id的字段作为主键 但很多情况下,默认的实现与实际场景不符,因此MybatisPlus提供了一些注解便于我们声明表信息。 @TableName 描述:表名注解,标识实体类对应的表 @TableId 描述:主键注解,标识实体类中的主键字段 TableId注解支持两个属性: 属性 类型 必须指定 默认值 描述 value String 否 "" 主键字段名 type Enum 否 IdType.NONE 指定主键类型 @TableName("user_detail") public class User { @TableId(value="id_dd",type=IdType.AUTO) private Long id; private String name; } 这个例子会,映射到数据库中的user_detail表,主键为id_dd,并且插入时采用数据库自增;能自动回写主键,相当于开启useGeneratedKeys=true,执行完 insert(user) 后,user.getId() 就会是数据库分配的主键值,否则默认获得null,但不影响数据表中的内容。 type=dType.ASSIGN_ID 表示用雪花算法生成密码,更加复杂,而不是简单的AUTO自增。它也能自动回写主键。 @TableField 普通字段注解 一般情况下我们并不需要给字段添加@TableField注解,一些特殊情况除外: 成员变量名与数据库字段名不一致 成员变量是以isXXX命名,按照JavaBean的规范,MybatisPlus识别字段时会把is去除,这就导致与数据库不符。 public class User { private Long id; private String name; private Boolean isActive; // 按 JavaBean 习惯,这里用 isActive,数据表是is_acitive,但MybatisPlus会识别为active } 成员变量名与数据库一致,但是与数据库的**关键字(如order)**冲突。 public class Order { private Long id; private Integer order; // 名字和 SQL 关键字冲突 } 默认MP会生成:SELECT id, order FROM order; 导致报错 一些字段不希望被映射到数据表中,不希望进行增删查改 解决办法: @TableField("is_active") private Boolean isActive; @TableField("`order`") //添加转义字符 private Integer order; @TableField(exist=false) //exist默认是true, private String address; 4.常用配置 大多数的配置都有默认值,因此我们都无需配置。但还有一些是没有默认值的,例如: 实体类的别名扫描包 全局id类型 要改也就改这两个即可 mybatis-plus: type-aliases-package: edu.whut.mp.domain.po global-config: db-config: id-type: auto # 全局id类型为自增长 作用:1.把edu.whut.mp.domain.po 包下的所有 PO 类注册为 MyBatis 的 Type Alias。这样在你的 Mapper XML 里就可以直接写 <resultType="User">(或 <parameterType="User">)而不用写全限定类名 edu.whut.mp.domain.po.User 2.无需在每个 @TableId 上都写 type = IdType.AUTO,统一由全局配置管。 核心功能 前面的例子都是根据主键id更新、修改、查询,无法支持复杂条件where。 条件构造器Wrapper 除了新增以外,修改、删除、查询的SQL语句都需要指定where条件。因此BaseMapper中提供的相关方法除了以id作为where条件以外,还支持更加复杂的where条件。 Wrapper就是条件构造的抽象类,其下有很多默认实现,继承关系如图: QueryWrapper 在AbstractWrapper的基础上拓展了一个select方法,允许指定查询字段,无论是修改、删除、查询,都可以使用QueryWrapper来构建查询条件。 select方法只需用于 查询 时指定所需的列,完整查询不需要,用于update和delete不需要。 QueryWrapper 里对 like、eq、ge 等方法都做了重载 QueryWrapper<User> qw = new QueryWrapper<>(); qw.like("name", name); //两参版本,第一个参数对应数据库中的列名,如果对应不上,就会报错!!! qw.like(StrUtil.isNotBlank(name), "name", name); //三参,多一个boolean condition 参数 **例1:**查询出名字中带o的,存款大于等于1000元的人的id,username,info,balance: /** * SELECT id,username,info,balance * FROM user * WHERE username LIKE ? AND balance >=? */ @Test void testQueryWrapper(){ QueryWrapper<User> wrapper =new QueryWrapper<User>() .select("id","username","info","balance") .like("username","o") .ge("balance",1000); //查询 List<User> users=userMapper.selectList(wrapper); users.forEach(System.out::println); } UpdateWrapper 基于BaseMapper中的update方法更新时只能直接赋值,对于一些复杂的需求就难以实现。 例1: 例如:更新id为1,2,4的用户的余额,扣200,对应的SQL应该是: UPDATE user SET balance = balance - 200 WHERE id in (1, 2, 4) @Test void testUpdateWrapper() { List<Long> ids = List.of(1L, 2L, 4L); // 1.生成SQL UpdateWrapper<User> wrapper = new UpdateWrapper<User>() .setSql("balance = balance - 200") // SET balance = balance - 200 .in("id", ids); // WHERE id in (1, 2, 4) // 2.更新,注意第一个参数可以给null,告诉 MP:不要从实体里取任何字段值 // 而是基于UpdateWrapper中的setSQL来更新 userMapper.update(null, wrapper); } 例2: // 用 UpdateWrapper 拼 WHERE + SET UpdateWrapper<User> wrapper = new UpdateWrapper<User>() // WHERE status = 'ACTIVE' .eq("status", "ACTIVE") // SET balance = 2000, name = 'Alice' .set("balance", 2000) .set("name", "Alice"); // 把 entity 参数传 null,MyBatis-Plus 会只用 wrapper 里的 set/where userMapper.update(null, wrapper); LambdaQueryWrapper(推荐) 是QueryWrapper和UpdateWrapper的上位选择!!! 传统的 QueryWrapper/UpdateWrapper 需要把数据库字段名写成字符串常量,既容易拼写出错,也无法在编译期校验。MyBatis-Plus 引入了两种基于 Lambda 的 Wrapper —— LambdaQueryWrapper 和 LambdaUpdateWrapper —— 通过传入实体类的 getter 方法引用,框架会自动解析并映射到对应的列,实现了类型安全和更高的可维护性。 // ——— 传统 QueryWrapper ——— public User findByUsername(String username) { QueryWrapper<User> qw = new QueryWrapper<>(); // 硬编码列名,拼写错了编译不过不了,会在运行时抛数据库异常 qw.eq("user_name", username); return userMapper.selectOne(qw); } // ——— LambdaQueryWrapper ——— public User findByUsername(String username) { // 内部已注入实体 Class 和元数据,方法引用自动解析列名 LambdaQueryWrapper<User> qw = Wrappers.lambdaQuery(User.class) .eq(User::getUserName, username); return userMapper.selectOne(qw); } 自定义sql 即自己编写Wrapper查询条件,再结合Mapper.xml编写SQL **例1:**以 UPDATE user SET balance = balance - 200 WHERE id in (1, 2, 4) 为例: 1)先在业务层利用wrapper创建条件,传递参数 @Test void testCustomWrapper() { // 1.准备自定义查询条件 List<Long> ids = List.of(1L, 2L, 4L); QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id", ids); // 2.调用mapper的自定义方法,直接传递Wrapper userMapper.deductBalanceByIds(200, wrapper); } 2)自定义mapper层把wrapper和其他业务参数传进去,自定义sql语句书写sql的前半部分,后面拼接。 public interface UserMapper extends BaseMapper<User> { /** * 注意:更新要用 @Update * - #{money} 会被替换为方法第一个参数 200 * - ${ew.customSqlSegment} 会展开 wrapper 里的 WHERE 子句 */ @Update("UPDATE user " + "SET balance = balance - #{money} " + "${ew.customSqlSegment}") void deductBalanceByIds(@Param("money") int money, @Param("ew") QueryWrapper<User> wrapper); } @Param("ew")就是给这个方法参数在 MyBatis 的 SQL 映射里起一个别名—— ew , Mapper 的注解或 XML 里,MyBatis 想要拿到这个参数,就用它的 @Param 名称——也就是 ew: @Param("ew")中ew是 MP 约定的别名! ${ew.customSqlSegment} 可以自动拼接传入的条件语句 **例2:**查询出所有收货地址在北京的并且用户id在1、2、4之中的用户 普通mybatis: <select id="queryUserByIdAndAddr" resultType="com.itheima.mp.domain.po.User"> SELECT * FROM user u INNER JOIN address a ON u.id = a.user_id WHERE u.id <foreach collection="ids" separator="," item="id" open="IN (" close=")"> #{id} </foreach> AND a.city = #{city} </select> mp方法: @Test void testCustomJoinWrapper() { // 1.准备自定义查询条件 QueryWrapper<User> wrapper = new QueryWrapper<User>() .in("u.id", List.of(1L, 2L, 4L)) .eq("a.city", "北京"); // 2.调用mapper的自定义方法 List<User> users = userMapper.queryUserByWrapper(wrapper); } @Select("SELECT u.* FROM user u INNER JOIN address a ON u.id = a.user_id ${ew.customSqlSegment}") List<User> queryUserByWrapper(@Param("ew")QueryWrapper<User> wrapper); Service层的常用方法 查询: selectById:根据主键 ID 查询单条记录。 selectBatchIds:根据主键 ID 批量查询记录。 selectOne:根据指定条件查询单条记录。 @Service public class UserService { @Autowired private UserMapper userMapper; public User findByUsername(String username) { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("username", username); return userMapper.selectOne(queryWrapper); } } selectList:根据指定条件查询多条记录。 QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.ge("age", 18); List<User> users = userMapper.selectList(queryWrapper); 插入: insert:插入一条记录。 User user = new User(); user.setUsername("alice"); user.setAge(20); int rows = userMapper.insert(user); 更新 updateById:根据主键 ID 更新记录。 User user = new User(); user.setId(1L); user.setAge(25); int rows = userMapper.updateById(user); update:根据指定条件更新记录。 UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(); updateWrapper.eq("username", "alice"); User user = new User(); user.setAge(30); int rows = userMapper.update(user, updateWrapper); 删除操作 deleteById:根据主键 ID 删除记录。 deleteBatchIds:根据主键 ID 批量删除记录。 delete:根据指定条件删除记录。 QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("username", "alice"); int rows = userMapper.delete(queryWrapper); IService 基本使用 由于Service中经常需要定义与业务有关的自定义方法,因此我们不能直接使用IService,而是自定义Service接口,然后继承IService以拓展方法。同时,让自定义的Service实现类继承ServiceImpl,这样就不用自己实现IService中的接口了。 首先,定义IUserService,继承IService: public interface IUserService extends IService<User> { // 拓展自定义方法 } 然后,编写UserServiceImpl类,继承ServiceImpl,实现UserService: @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService { } Controller层中写: @RestController @RequestMapping("/users") @Slf4j @Api(tags = "用户管理接口") public class UserController { @Autowired private IUserService userService; @PostMapping @ApiOperation("新增用户接口") public void saveUser(@RequestBody UserFormDTO userFormDTO){ User user=new User(); BeanUtils.copyProperties(userFormDTO, user); userService.save(user); } @DeleteMapping("{id}") @ApiOperation("删除用户接口") public void deleteUserById(@PathVariable Long id){ userService.removeById(id); } @GetMapping("{id}") @ApiOperation("根据id查询接口") public UserVO queryUserById(@PathVariable Long id){ User user=userService.getById(id); UserVO userVO=new UserVO(); BeanUtils.copyProperties(user,userVO); return userVO; } @PutMapping("/{id}/deduction/{money}") @ApiOperation("根据id扣减余额") public void updateBalance(@PathVariable Long id,@PathVariable Long money){ userService.deductBalance(id,money); } } service层: @Service public class IUserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService { @Autowired private UserMapper userMapper; @Override public void deductBalance(Long id, Long money) { //1.查询用户 User user=getById(id); if(user==null || user.getStatus()==2){ throw new RuntimeException("用户状态异常!"); } //2.查验余额 if(user.getBalance()<money){ throw new RuntimeException("用户余额不足!"); } //3.扣除余额 update User set balance=balance-money where id=id userMapper.deductBalance(id,money); } } mapper层: @Mapper public interface UserMapper extends BaseMapper<User> { @Update("update user set balance=balance-#{money} where id=#{id}") void deductBalance(Long id, Long money); } 总结:如果是简单查询,如用id来查询、删除,可以直接在Controller层用Iservice方法,否则自定义业务层Service实现具体任务。 Service层的lambdaQuery IService中还提供了Lambda功能来简化我们的复杂查询及更新功能。 相当于「条件构造」和「执行方法」写在一起 this.lambdaQuery() = LambdaQueryWrapper + 内置的执行方法(如 .list()、.one()) 特性 lambdaQuery() lambdaUpdate() 主要用途 构造查询条件,执行 SELECT 操作 构造更新条件,执行 UPDATE(或逻辑删除)操作 支持的方法 .eq(), .like(), .gt(), .orderBy(), .select() 等 .eq(), .lt(), .set(), .setSql() 等 执行方法 .list(), .one(), .page() 等 .update(), .remove()(逻辑删除 **案例一:**实现一个根据复杂条件查询用户的接口,查询条件如下: name:用户名关键字,可以为空 status:用户状态,可以为空 minBalance:最小余额,可以为空 maxBalance:最大余额,可以为空 @GetMapping("/list") @ApiOperation("根据id集合查询用户") public List<UserVO> queryUsers(UserQuery query){ // 1.组织条件 String username = query.getName(); Integer status = query.getStatus(); Integer minBalance = query.getMinBalance(); Integer maxBalance = query.getMaxBalance(); // 2.查询用户 List<User> users = userService.lambdaQuery() .like(username != null, User::getUsername, username) .eq(status != null, User::getStatus, status) .ge(minBalance != null, User::getBalance, minBalance) .le(maxBalance != null, User::getBalance, maxBalance) .list(); // 3.处理vo return BeanUtil.copyToList(users, UserVO.class); } .eq(status != null, User::getStatus, status),使用User::getStatus方法引用并不直接把'Status'插入到 SQL,而是在运行时会被 MyBatis-Plus 解析成实体属性 Status”对应的数据库列是 status。推荐!!! 可以发现lambdaQuery方法中除了可以构建条件,还需要在链式编程的最后添加一个list(),这是在告诉MP我们的调用结果需要是一个list集合。这里不仅可以用list(),可选的方法有: .one():最多1个结果 .list():返回集合结果 .count():返回计数结果 MybatisPlus会根据链式编程的最后一个方法来判断最终的返回结果。 这里不够规范,业务写在controller层中了。 **案例二:**改造根据id修改用户余额的接口,如果扣减后余额为0,则将用户status修改为冻结状态(2) @Override @Transactional public void deductBalance(Long id, Integer money) { // 1.查询用户 User user = getById(id); // 2.校验用户状态 if (user == null || user.getStatus() == 2) { throw new RuntimeException("用户状态异常!"); } // 3.校验余额是否充足 if (user.getBalance() < money) { throw new RuntimeException("用户余额不足!"); } // 4.扣减余额 update tb_user set balance = balance - ? int remainBalance = user.getBalance() - money; lambdaUpdate() .set(User::getBalance, remainBalance) // 更新余额 .set(remainBalance == 0, User::getStatus, 2) // 动态判断,是否更新status .eq(User::getId, id) .eq(User::getBalance, user.getBalance()) // 乐观锁 .update(); } 批量新增 每 batchSize 条记录作为一个 JDBC batch 提交一次(1000 条就一次) @Test void testSaveBatch() { // 准备10万条数据 List<User> list = new ArrayList<>(1000); long b = System.currentTimeMillis(); for (int i = 1; i <= 100000; i++) { list.add(buildUser(i)); // 每1000条批量插入一次 if (i % 1000 == 0) { userService.saveBatch(list); list.clear(); } } long e = System.currentTimeMillis(); System.out.println("耗时:" + (e - b)); } 之所以把 100 000 条记录分成每 1 000 条一批来插,是为了兼顾 性能、内存 和 数据库/JDBC 限制。 JDBC 或数据库参数限制 很多数据库(MySQL、Oracle 等)对单条 SQL 里 VALUES 列表的长度有上限,一次性插入几十万行可能导致 SQL 过长、参数个数过多,被驱动或数据库拒绝。 即使驱动不直接报错,也可能因为网络包(packet)过大而失败。 内存占用和 GC 压力 JDBC 在执行 batch 时,会把所有要执行的 SQL 和参数暂存在客户端内存里。如果一次性缓存 100 000 条记录的参数(可能是几 MB 甚至十几 MB),容易触发 OOM 或者频繁 GC。 事务日志和回滚压力 一次性插入大量数据,数据库需要在事务日志里记录相应条目,回滚时也要一次性回滚所有操作,性能开销巨大。分批能让每次写入都较为“轻量”,回滚范围也更小。 这种本质上是多条单行 INSERT Preparing: INSERT INTO user ( username, password, phone, info, balance, create_time, update_time ) VALUES ( ?, ?, ?, ?, ?, ?, ? ) Parameters: user_1, 123, 18688190001, "", 2000, 2023-07-01, 2023-07-01 Parameters: user_2, 123, 18688190002, "", 2000, 2023-07-01, 2023-07-01 Parameters: user_3, 123, 18688190003, "", 2000, 2023-07-01, 2023-07-01 而如果想要得到最佳性能,最好是将多条SQL合并为一条,像这样: INSERT INTO user ( username, password, phone, info, balance, create_time, update_time ) VALUES (user_1, 123, 18688190001, "", 2000, 2023-07-01, 2023-07-01), (user_2, 123, 18688190002, "", 2000, 2023-07-01, 2023-07-01), (user_3, 123, 18688190003, "", 2000, 2023-07-01, 2023-07-01), (user_4, 123, 18688190004, "", 2000, 2023-07-01, 2023-07-01); 需要修改项目中的application.yml文件,在jdbc的url后面添加参数&rewriteBatchedStatements=true: url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true 但是会存在上述上事务的问题!!! MQ分页 快速入门 1)引入依赖 <!-- 数据库操作:https://mp.baomidou.com/ --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.9</version> </dependency> <!-- MyBatis Plus 分页插件 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-jsqlparser-4.9</artifactId> </dependency> 2)定义通用分页查询条件实体 @Data @ApiModel(description = "分页查询实体") public class PageQuery { @ApiModelProperty("页码") private Long pageNo; @ApiModelProperty("页码") private Long pageSize; @ApiModelProperty("排序字段") private String sortBy; @ApiModelProperty("是否升序") private Boolean isAsc; } 3)新建一个 UserQuery 类,让它继承自你已有的 PageQuery @Data @ApiModel(description = "用户分页查询实体") public class UserQuery extends PageQuery { @ApiModelProperty("用户名(模糊查询)") private String name; } 4)Service里使用 @Service public class UserService extends ServiceImpl<UserMapper, User> { /** * 用户分页查询(带用户名模糊 + 动态排序) * * @param query 包含 pageNo、pageSize、sortBy、isAsc、name 等字段 */ public Page<User> pageByQuery(UserQuery query) { // 1. 构造 Page 对象 Page<User> page = new Page<>( query.getPageNo(), query.getPageSize() ); // 2. 构造查询条件 LambdaQueryWrapper<User> qw = Wrappers.<User>lambdaQuery() // 当 name 非空时,加上 user_name LIKE '%name%' .like(StrUtil.isNotBlank(query.getName()), User::getUserName, query.getName()); // 3. 动态排序 if (StrUtil.isNotBlank(query.getSortBy())) { String column = StrUtil.toUnderlineCase(query.getSortBy()); boolean asc = Boolean.TRUE.equals(query.getIsAsc()); qw.last("ORDER BY " + column + (asc ? " ASC" : " DESC")); } // 4. 执行分页查询 return this.page(page, qw); } }
自学
zy123
5月21日
0
3
0
2025-05-10
ZY网络重构分析
多智能体随机网络的全局知识对其模型收敛性影响的研究 智能体网络的现状、包括网络结构(和现有互联网、物联网的差异)、通信协议(A2A(agent)、MCP成为主流,为了智能体之间的通信)传统的协议已经慢慢被替代,不止是传统互联网应用-》大模型 多智能体随机网络与传统互联网不一样,结构基于随机网络(有什么作用,举一些具体的例子),通信协议(没有专门的协议,我们工作的出发点)、应用(联邦学习、图神经网络、强化学习) 网络模型的收敛性,怎么定义收敛性?收敛速度、收敛效率(考虑代价)、收敛的稳定性(换了个环境变化大),联邦学习、强化学习收敛性的问题,和哪些因素有关,网络全局结构对它的影响;推理阶段也有收敛性,多智能体推理结果是否一致;图神经网络推理结果是否一致。 多智能体随机网络全局知识的获取(分布式、集中式) 多智能体随机机会网络、动态谱参数估算、网络重构算法、聚类量化算法、联邦学习、图神经网络 如何确定kmeans的簇数?节点之间的流量,空间转为时间的图。 压缩感知 函数拟合 采样定理 傅里叶变换 谱分解与网络重构 实对称矩阵性质: 对于任意 $n \times n$ 的实对称矩阵 $A$: 秩可以小于 $n$(即存在零特征值,矩阵不可逆)。 但仍然有 $n$ 个线性无关的特征向量(即可对角化)。 特征值有正有负!!! 一个实对称矩阵可以通过其特征值和特征向量进行分解。对于一个 $n \times n$ 的对称矩阵 $A$, 完整谱分解可以表示为: $$ A = Q \Lambda Q^T \\ A = \sum_{i=1}^{n} \lambda_i x_i x_i^T $$ $Q$是$n \times n$的正交矩阵,每一列是一个特征向量;$\Lambda$是$n \times n$的对角矩阵,对角线元素是特征值$\lambda_i$ ,其余为0。 其中,$\lambda_i$ 是矩阵 $A$ 的第 $i$ 个特征值,$x_i$ 是对应的特征向量。(注意!这里的特征向量需要归一化!!!) 如果矩阵 $A$ 的秩为 $r$ ,那么谱分解里恰好有 $r$ 个非零特征值。 用这 $r$ 对特征值/特征向量就能精确重构出 $A$,因为零特征值对矩阵重构不提供任何贡献。 因此,需要先对所有特征值取绝对值,从大到小排序,取前 $r$ 个!!! 截断的谱分解(取前 $\kappa$ 个特征值和特征向量) 如果我们只保留前 $\kappa$ 个绝对值最大的特征值和对应的特征向量,那么: 特征向量矩阵 $U_\kappa$:取 $U$ 的前 $\kappa$ 列,维度为 $n \times \kappa$。 特征值矩阵 $\Lambda_\kappa$:取 $\Lambda$ 的前 $\kappa \times \kappa$ 子矩阵(即前 $\kappa$ 个对角线元素),维度为 $\kappa \times \kappa$。 因此,截断后的近似分解为: $$ A \approx U_\kappa \Lambda_\kappa U_\kappa^T\\ A \approx \sum_{i=1}^{\kappa} \lambda_i x_i x_i^T $$ 推导过程 特征值和特征向量的定义 对于一个对称矩阵 $A$,其特征值和特征向量满足: $$ A x_i = \lambda_i x_i $$ 其中,$\lambda_i$ 是特征值,$x_i$ 是对应的特征向量。 谱分解 将这些特征向量组成一个正交矩阵 $Q$ $A = Q \Lambda Q^T$ $$ Q = \begin{bmatrix} x_1 & x_2 & \cdots & x_n \end{bmatrix}, $$ $$ Q \Lambda = \begin{bmatrix} \lambda_1 x_1 & \lambda_2 x_2 & \cdots & \lambda_n x_n \end{bmatrix}. $$ $$ Q \Lambda Q^T = \begin{bmatrix} \lambda_1 x_1 & \lambda_2 x_2 & \cdots & \lambda_n x_n \end{bmatrix} \begin{bmatrix} x_1^T \\ x_2^T \\ \vdots \\ x_n^T \end{bmatrix}. $$ $$ Q \Lambda Q^T = \lambda_1 x_1 x_1^T + \lambda_2 x_2 x_2^T + \cdots + \lambda_n x_n x_n^T. $$ 可以写为 $$ A = \sum_{i=1}^{n} \lambda_i x_i x_i^T. $$ 网络重构 在随机网络中,网络的邻接矩阵 $A$ 通常是对称的。利用预测算法得到的谱参数 ${\lambda_i, x_i}$ 后,就可以用以下公式重构网络矩阵: $$ A(G) = \sum_{i=1}^{n} \lambda_i x_i x_i^T $$ 性质 特征分解/谱分解 奇异值分解(SVD) 适用矩阵 仅限方阵($n \times n$) 任意矩阵($m \times n$,包括矩形矩阵) 分解形式 $A = P \Lambda P^{-1}$ $A = U \Sigma V^*$ 矩阵类型 可对角化矩阵(如对称、正规矩阵) 所有矩阵(包括不可对角化的方阵和非方阵) 输出性质 特征值($\lambda_i$)可能是复数 奇异值($\sigma_i$)始终为非负实数 正交性 仅当 $A$ 正规时 $P$ 是酉矩阵 $U$ 和 $V$ 始终是酉矩阵(正交) 谱分解的对象为实对称矩阵 奇异值分解 步骤 步骤 1:验证矩阵对称性 确保 $A$ 是实对称矩阵(即 $A = A^\top$),此时SVD可通过特征分解直接构造。 步骤 2:计算特征分解 对 $A$ 进行特征分解: $$ A = Q \Lambda Q^\top $$ 其中: $Q$ 是正交矩阵($Q^\top Q = I$),列向量为 $A$ 的特征向量。 $\Lambda = \text{diag}(\lambda_1, \lambda_2, \dots, \lambda_n)$,$\lambda_i$ 为 $A$ 的特征值(可能有正、负或零)。 步骤 3:构造奇异值矩阵 $\Sigma$ 奇异值:取特征值的绝对值 $\sigma_i = |\lambda_i|$,得到对角矩阵: $$ \Sigma = \text{diag}(\sigma_1, \sigma_2, \dots, \sigma_n) $$ 排列顺序:通常按 $\sigma_i$ 降序排列(可选,但推荐)。 步骤 4:处理符号(负特征值) 符号矩阵 $S$:定义对角矩阵 $S = \text{diag}(s_1, s_2, \dots, s_n)$,其中: $$ s_i = \begin{cases} 1 & \text{if } \lambda_i \geq 0, \ -1 & \text{if } \lambda_i < 0. \end{cases} $$ 左奇异向量矩阵 $U$:调整特征向量的方向: $$ U = Q S $$ 即 $U$ 的列为 $Q$ 的列乘以对应特征值的符号。 步骤 5:确定右奇异向量矩阵 $V$ 由于 $A$ 对称,右奇异向量矩阵 $V$ 直接取特征向量矩阵: $$ V = Q $$ 步骤 6:组合得到SVD 最终SVD形式为: $$ A = U \Sigma V^\top $$ 验证: $$ U \Sigma V^\top = (Q S) \Sigma Q^\top = Q (S \Sigma) Q^\top = Q \Lambda Q^\top = A $$ (因为 $S \Sigma = \Lambda$,例如 $\text{diag}(-1) \cdot \text{diag}(2) = \text{diag}(-2)$)。 例子(含正、负、零特征值) 设对称矩阵 $$ A = \begin{bmatrix} 1 & 0 & 2 \\ 0 & -1 & 0 \\ 2 & 0 & 1 \end{bmatrix} $$ 特征分解 特征值: $$ \lambda_1 = 3, \quad \lambda_2 = -1, \quad \lambda_3 = 0 $$ 特征向量矩阵: $$ Q = \begin{bmatrix} \frac{1}{\sqrt{2}} & 0 & \frac{1}{\sqrt{2}} \ 0 & 1 & 0 \ \frac{1}{\sqrt{2}} & 0 & -\frac{1}{\sqrt{2}} \end{bmatrix} $$ 构造SVD 步骤: 按 $|\lambda_i|$ 降序排列:$3, 1, 0$(取绝对值后排序)。 奇异值矩阵: $$ \Sigma = \text{diag}(3, 1, 0) $$ 符号调整矩阵: $$ S = \text{diag}(1, -1, 1) \quad (\lambda_3=0 \text{ 的符号可任选}) $$ 左奇异向量矩阵: $$ U = Q S = \begin{bmatrix} \frac{1}{\sqrt{2}} & 0 & \frac{1}{\sqrt{2}} \ 0 & -1 & 0 \ \frac{1}{\sqrt{2}} & 0 & -\frac{1}{\sqrt{2}} \end{bmatrix} $$ 右奇异向量矩阵: $$ V = Q $$ 验证: $$ U \Sigma V^\top = A $$ 网络重构分析 基于扰动理论的特征向量估算方法 设原矩阵为 $A$,扰动后矩阵为 $A+\zeta C$(扰动矩阵 $\zeta C$,$\zeta$是小参数),令其第 $i$ 个特征值、特征向量分别为 $\lambda_i,x_i$ 和 $\tilde\lambda_i,\tilde x_i$。 特征向量的一阶扰动公式: $$ \Delta x_i =\tilde x_i - x_i \;\approx\; \zeta \sum_{k\neq i} \frac{x_k^T\,C\,x_i}{\lambda_i - \lambda_k}\;x_k, $$ 输出:对应第 $i$ 个特征向量修正量 $\Delta x_i$。 特征值的一阶扰动公式: $$ \Delta\lambda_i = \tilde\lambda_i - \lambda_i \;\approx\;\zeta\,x_i^T\,C\,x_i $$ **关键假设:**当扰动较小( $\zeta\ll1$) 且各模态近似正交均匀时,常作进一步近似 $$ x_k^T\,C\,x_i \;\approx\; x_i^T\,C\,x_i \; $$ 正交: $\{x_k\}$ 本身是正交基,这是任何对称矩阵特征向量天然具有的属性。 均匀:我们把 $C$ 看作“不偏向任何特定模态”的随机小扰动——换句话说,投影到任何两个方向 $(x_i,x_k)$ 上的耦合强度 $x_k^T,C,x_i\quad\text{和}\quad x_i^T,C,x_i$ 在数值量级上应当差不多,因此可以互相近似。 因此,将所有的 $x_k^T C x_i$ 替换为 $x_i^T C x_i$: $$ \Delta x_i \approx \zeta \sum_{k\neq i} \frac{x_i^T C x_i}{\lambda_i - \lambda_k} x_k = \zeta (x_i^T C x_i) \sum_{k\neq i} \frac{1}{\lambda_i - \lambda_k} x_k = \sum_{k\neq i} \frac{\Delta \lambda_i}{\lambda_i - \lambda_k} x_k \tag{*} $$ $$ \Delta x_i \approx\sum_{k\neq i} \frac{\Delta \lambda_i}{\lambda_i - \lambda_k} x_k \tag{*} $$ 问题: 当前时刻的邻接矩阵 $$ A^{(1)}\in\mathbb R^{n\times n},\qquad A^{(1)},x_i^{(1)}=\lambda_i^{(1)},x_i^{(1)},\quad |x_i^{(1)}|=1. $$ 下一时刻的邻接矩阵 $$ A^{(2)}\in\mathbb R^{n\times n}, $$ 已知它的第 $i$ 个特征值 $\lambda_i^{(2)}$(卡尔曼滤波得来). 求当前时刻的特征向量 $x_i^{(2)}$。 下一时刻第 $i$ 个特征向量的预测为 $$ \boxed{ x_i^{(2)} \;=\; x_i^{(1)}+\Delta x_i \;\approx\; x_i^{(1)} +\sum_{k\neq i} \frac{\lambda_i^{(2)}-\lambda_i^{(1)}} {\lambda_i^{(1)}-\lambda_k^{(1)}}\; x_k^{(1)}. } $$ 通过该估算方法可以依次求出下一时刻的所有特征向量。 矩阵符号说明 原始(真实)邻接矩阵 $A$ ,假设 $A$ 的秩为 $r$: $\lambda_{r+1}=\cdots=\lambda_n=0$ $$ A = \sum_{m=1}^n \lambda_m,x_m x_m^T=\begin{align*} \sum_{m=1}^r \lambda_m x_m x_m^T + \sum_{m=r+1}^n \lambda_m x_m x_m^T = \sum_{m=1}^r \lambda_m x_m x_m^T \end{align*}, $$ 滤波估计得到的矩阵及谱分解: $$ \widetilde A = \sum_{m=1}^r \widetilde\lambda_m,\widetilde x_m\widetilde x_m^T, \quad \widetilde\lambda_1\ge\cdots\ge\widetilde\lambda_n; $$ 只取前 $\kappa$ 项重构 : $$ A_\kappa ;=;\sum_{m=1}^\kappa \widetilde\lambda_m,\widetilde x_m\widetilde x_m^T, $$ 对 $A_\kappa$ 进行K-means聚类,得到 $A_{final}$ 目标是让 $A_{final}$ = $A$ 0/1矩阵 其中 $\widetilde{\lambda}_i$ 和 $\widetilde _i$ 分别为通过预测得到矩阵 $\widetilde A$ 的第 $i$ 个特征值和对应特征向量。 然而预测值和真实值之间存在误差,直接进行矩阵重构会使得重构误差较大。 对于这个问题,文献提出一种 0/1 矩阵近似恢复算法。 $$ a_{ij} = \begin{cases} 1, & \text{if}\ \lvert a_{ij} - 1 \rvert < 0.5 \\ 0, & \text{else} \end{cases} $$ 只要我们的估计值与真实值之间差距**小于 0.5**,就能保证阈值处理以后准确地恢复原边信息。 文中提出网络特征值扰动与邻接矩阵扰动具有相同的规律 真实矩阵 $A$ 与预测矩阵 $\widetilde{A} $ 之间的差为 (秩为 $r$) $$ A - \widetilde{A}=\sum_{m=1}^r \lambda_m\,x_m x_m^T-\sum_{m=1}^r \widetilde\lambda_m\,\widetilde x_m\widetilde x_m^T $$ **若假设特征向量扰动可忽略,即$\widetilde x_m\approx x_m$ ,扰动可简化为(这里可能有问题,特征向量的扰动也要计算)** $$ A - \widetilde{A} = \sum_{m=1}^r \Delta \lambda_m _m _m^T. $$ 对于任意元素 $(i, j)$ 上有 $$ |a_{ij} - \widetilde{a}_{ij}|=\left| \sum_{m=1}^r \Delta \lambda_m ( _m _m^T)_{ij} \right| < \frac{1}{2} $$ 于一个归一化的特征向量 $ _m$,其外积矩阵$ _m _m^T$ 满足 $$ |( _m _m^T)_{ij}| \leq 1. $$ 例: $$ x_m = \begin{bmatrix} \frac{1}{\sqrt{2}} \\ \frac{1}{\sqrt{2}} \end{bmatrix}\\ x_m x_m^T = \begin{bmatrix} \frac{1}{\sqrt{2}} \\ \frac{1}{\sqrt{2}} \end{bmatrix} \begin{bmatrix} \frac{1}{\sqrt{2}} & \frac{1}{\sqrt{2}} \end{bmatrix} = \begin{bmatrix} \frac{1}{2} & \frac{1}{2} \\ \frac{1}{2} & \frac{1}{2} \end{bmatrix} $$ 每个元素的绝对值$\leq1$ $$ \left| \sum_{m=1}^r \Delta \lambda_m (x_m x_m^T)_{ij} \right| \leq \sum_{m=1}^r |\Delta \lambda_m| \cdot |(x_m x_m^T)_{ij}| \leq \sum_{m=1}^r |\Delta \lambda_m|. $$ 为了确保 $|a_{ij} - \widetilde{a}_{ij}| < \frac{1}{2}$ 对所有 $(i,j)$ 成立,网络精准重构条件为: $$ \sum_{m=1}^r\left| \Delta \lambda_m\right| < \frac{1}{2} $$ 0-1 矩阵能够精准重构的容忍上界与网络中的节点数量成反比,网络中节点数量越多,实现精准重构的要求也就越高。 如果在高层次(特征值滤波)的误差累积超过了一定阈值,就有可能在低层次(邻接矩阵元素)中出现翻转。公式推导了只要谱参数的误差之和不超过 0.5,就可以保证0-1矩阵的精确重构。 非0/1矩阵 量化误差 对估计矩阵 $\tilde{A}$ 的所有元素 ${\tilde{a}{ij}}$ 进行 $K$-means 聚类,得到每个簇中心 ${c_k}{k=1}^K$。 簇内平均偏差: 对于第 $k$簇,算该簇所有点到中心的平均绝对偏差: $$ \text{mean}_k = \frac{1}{|\mathcal{S}k|} \sum{(i,j)\in\mathcal{S}k} |\tilde{a}{ij} - c_k| $$ 全局允许误差: $$ \delta_{\max} = \frac{1}{K} \sum_{k=1}^K \text{mean}_k $$ 对于归一化向量 $x_m$ 的研究 $$ x_m = \begin{bmatrix} x_{m,1} \\ x_{m,2} \\ \vdots \\ x_{m,d} \end{bmatrix} $$ $$ x_m x_m^T = \begin{bmatrix} x_{m,1}^2 & x_{m,1}x_{m,2} & \cdots & x_{m,1}x_{m,d} \\ x_{m,2}x_{m,1} & x_{m,2}^2 & \cdots & x_{m,2}x_{m,d} \\ \vdots & \vdots & \ddots & \vdots \\ x_{m,d}x_{m,1} & x_{m,d}x_{m,2} & \cdots & x_{m,d}^2 \end{bmatrix} $$ $$ (x_m x_m^T)_{ij}=x_{m,i}\,x_{m,j}\,, $$ **对于任意元素:**由于 $|x_m|2=1$ 且 $|x{m,i}|\le1$,必有 $$ |x_{m,i}x_{m,j}|\le1\,. $$ 对于非对角线元素: 对任意 $i\neq j$,由 $$ x_{m,i}^2 + x_{m,j}^2 \le \sum_{k=1}^d x_{m,k}^2 = 1 $$ $$ (|x_{m,i}| - |x_{m,j}|)^2 \geq 0 \implies |x_{m,i}|^2 + |x_{m,j}|^2 \geq 2 |x_{m,i}| |x_{m,j}|. $$ $$ |x_{m,i}x_{m,j}| = |x_{m,i}||x_{m,j}| \leq \frac{|x_{m,i}|^2 + |x_{m,j}|^2}{2} = \frac{x_{m,i}^2 + x_{m,j}^2}{2}. $$ $$ |(x_m x_m^T)_{ij}|=|x_{m,i}x_{m,j}| \le \frac{x_{m,i}^2 + x_{m,j}^2}{2} \le \frac12\,, $$ 带权重构需控制两类误差: 这里暂时忽略特征向量的扰动,即 $x_m=\widetilde x_m$ 滤波误差$\eta$: 来源:滤波器在谱域对真实特征值/向量的估计偏差,包括 特征值偏差 $\Delta\lambda_m=\lambda_m-\widetilde\lambda_m$ 特征向量:矩阵扰动得来 设矩阵 $A$ 的秩为 $r$ $$ A - \tilde A=\sum_{m=1}^r \lambda_m,x_m x_m^T-\sum_{m=1}^r \tilde\lambda_m,\tilde x_m\tilde x_m^T=\sum_{m=1}^r \Delta \lambda_m _m _m^T. $$ $$ \eta_F=\Bigl|\sum_{m=1}^r \Delta\lambda_m, x_m x_m^T\Bigr|F = \sqrt{\sum{m=1}^r (\Delta\lambda_m)^2}. $$ 截断谱分解误差 $\epsilon$: 只取前 $\kappa$ 个特征对重构 $$ A_\kappa \;=\;\sum_{m=1}^\kappa \widetilde\lambda_m\,\widetilde x_m\widetilde x_m^T=\;\sum_{m=1}^\kappa \widetilde\lambda_m\, x_m x_m^T $$ $$ \widetilde A - A_\kappa=\sum_{m=\kappa+1}^r \widetilde\lambda_m\, x_m x_m^T $$ $$ \Bigl\|\sum_{m=\kappa+1}^r \widetilde\lambda_m\, x_m x_m^T\Bigr\|_F=\sqrt{\sum_{m = \kappa + 1}^{r} \widetilde{\lambda}_{m}^{2}}.\epsilon = \Bigl\|\sum_{m=\kappa+1}^r \widetilde\lambda_m\, x_m x_m^T\Bigr\|_F=\sqrt{\sum_{m = \kappa + 1}^{r} \widetilde{\lambda}_{m}^{2}}. $$ 总的误差 $$ \text{误差矩阵: } A - A_\kappa = \left( A - \tilde{A} \right) + \left( \tilde{A} - A_\kappa \right) = \sum_{m=1}^r \Delta \lambda_m x_m x_m^T + \sum_{m=\kappa+1}^r \tilde{\lambda}_m x_m x_m^T $$ 研究单个元素误差: $$ \text{元素误差绝对值:}|(A - A_\kappa){ij}| \leq \sum{m=1}^r |\Delta \lambda_m| \cdot |(x_m x_m^T){ij}| + \sum{m=\kappa+1}^r |\widetilde\lambda_m| \cdot |(x_m x_m^T){ij}| $$ 对于非对角线元素,由于 $|(x_m x_m^T){ij}| \leq \frac12$(归一化特征向量): $$ |(A - A_\kappa){ij}| \leq \frac{1}{2}\left(\sum{m=1}^r |\Delta \lambda_m| + \sum_{m=\kappa+1}^r |\widetilde\lambda_m|\right) $$ 研究整个矩阵误差: $$ | A - A_\kappa |F^2 = \left| \sum{m=1}^r \Delta \lambda_m x_m x_m^T + \sum_{m=\kappa+1}^r \widetilde{\lambda}_m x_m x_m^T \right|F^2 $$ 而 $| x_m x_m^T |F = \sqrt{\text{tr}(x_m x_m^T x_m x_m^T)} = \sqrt{\text{tr}(x_m x_m^T)} = | x_m |2 = 1$,因此: $$ | A - A\kappa |F^2 = \sum{m=1}^r \Delta \lambda_m^2 + \sum{m=\kappa+1}^r \widetilde{\lambda}m^2 $$ 最终: $$ | A - A\kappa |F = \sqrt{ \sum{m=1}^r |\Delta \lambda_m|^2 + \sum{m=\kappa+1}^r |\widetilde{\lambda}_m|^2 } $$ 最终约束条件: $$ |(A - A_\kappa)_{ij}| \leq 容忍误差\tau $$ $$ \frac{1}{2}\left(\sum_{m=1}^r |\Delta \lambda_m| + \sum_{m=\kappa+1}^r |\widetilde\lambda_m|\right)\leq 容忍误差\tau $$ $$ \delta_F = \lVert A - A_k \rVert_F = \sqrt{\sum_{i,j} \bigl(A - A_k\bigr)_{ij}^{2}} \;\le\; \sqrt{N^{2}\,\tau^{2}} \;=\; N\tau $$ 容忍误差推导 容忍误差 要保证重构矩阵 $A_\kappa$ 在每个位置上“落到”正确的某一 簇而不搞混,就必须让重构误差的最大绝对值严格小于任意两个相邻量化级别之间的半距(half‐spacing)。具体地: 量化级别及间距定义 设原始矩阵元素只能取 $K$ 个离散值: $$ v_1 < v_2 < \cdots < v_K, $$ 相邻级别间距为: $$ d_m = v_{m+1} - v_m,\quad m=1,\dots,K-1. $$ 最小间距确定 计算所有相邻级别的最小间距: $$ d_{\min} = \min_{1\le m\le K-1} d_m. $$ 通用容忍误差 为确保重构值不会"越界"到相邻级别,取阈值: $$ \boxed{\tau = \frac{d_{\min}}{2}.} $$ 量化误差和数据的分布(不是重构出来的矩阵值)有关,和容忍误差无关。 示例: 0-1矩阵,$a=0, b=1,K=2$,容忍误差为0.5。 量化级别 ${0,,0.3,,0.7,,1}$ ,$K=4$ 时: 相邻间距:$0.3, 0.4, 0.3$ $d_{\min}=0.3$,故 $\tau=0.15$ 量化误差 量化阈值:重构矩阵与真实矩阵的对应元素误差最大值为 $\tau$ ; 量化误差:真实矩阵与经量化之后的对应元素误差最大值为 $\delta$ 当我们把一个数值 $x$ 量化到某个中心 $v_m$ 时,它本身就会产生一个"量化误差": $$ \underbrace{|x - v_m|}_{\text{量化误差}}. $$ 1.当 $x$ 落在第 $m$ 个区间时 假设某个实数 $x$ 满足 $$ t_{m-1} \leq x < t_{m} $$ (其中我们约定 $t_{0} = -\infty$, $t_{K} = +\infty$),这时把 $x$ 量化到中心 $v_{m}$。 2.计算量化误差 由于 $t_{m} = \frac{v_{m} + v_{m+1}}{2}$ 且 $t_{m-1} = \frac{v_{m-1} + v_{m}}{2}$,当 $x \in [t_{m-1}, t_{m})$ 时,离它所在的中心 $v_{m}$ 的最大距离,就是它正好位于这段区间的"最远端"——也就是: $$ \max_{x \in [t_{m-1}, t_{m})} |x - v_{m}| = \max \left\{ |t_{m-1} - v_{m}|, |t_{m} - v_{m}| \right\}. $$ 但 $t_{m} - v_{m} = \frac{v_{m+1} - v_{m}}{2} = \frac{d_{m}}{2}$, 同理 $v_{m} - t_{m-1} = \frac{v_{m} - v_{m-1}}{2} = \frac{d_{m-1}}{2}$。 所以对任意落在第 $m$ 个决策区间里的 $x$,其量化到 $v_{m}$ 时的偏差至多为 $$ |x - v_{m}| \leq \max \left( \frac{d_{m-1}}{2}, \frac{d_{m}}{2} \right) \leq \frac{d_{\min}}{2}. $$ 综上,对所有可能的 $x$: $$ \max_ |x - Q(x)| = \frac{d_{\min}}{2}. $$ 于是定义 $\delta = \frac{d_{\min}}{2}$ 已知量化误差推导容忍误差和码本 推导容忍误差 已知:所有待量化的数都落在 $[X_{\min}, X_{\max}]$ :量化误差上限 $$ \delta = \max_ |x - Q(x)|. $$ 1.因为对所有落在第 $m$ 个区间的点,都有 $$ |x - v_{m}| \leq \delta $$ 由"落在第 $m$ 区间"可推出 $$ |x - v_{m}| \leq \max \left\{\frac{v_{m+1} - v_{m}}{2}, \frac{v_{m} - v_{m-1}}{2}\right\} \leq \delta. $$ 因此 $$ \frac{v_{m+1} - v_{m}}{2} \geq \delta, \quad \frac{v_{m} - v_{m-1}}{2} \geq \delta, \quad \forall m. $$ 2.这意味着"任意相邻中心间距"都不小于 $2\delta$,即 $$ v_{m+1} - v_{m} \geq 2 \delta, \quad \forall m \implies d_{\min} = 2 \delta. $$ 3."容忍阈值"定义为使得重构值 $\widehat $ 与真实中心 $v_{m}$ 之间只要偏差在 $\tau$ 以内,量化就不会跳到旁边。显然 $$ \tau = \min_{m}\left\{\frac{v_{m+1} - v_{m}}{2}\right\} = \frac{d_{\min}}{2} = \frac{2 \delta}{2} = \delta. $$ 构造码本: 1.确定量化间隔 因为"量化误差的最大值"为 $\delta$,在一维的均匀量化里相邻两个中心(码本)中点到中心的距离就是量化误差的上界,所以我们令 $$ \Delta = 2\,\delta. $$ 相邻两个量化中心之间的距离都取 $\Delta$,这样任何一个点到它最近中心的距离最多是 $\tfrac{\Delta}{2} = \delta$。 2.确定量化级别数 $K$ 为了覆盖整个区间 $[X_{\min},,X_{\max}]$,这些间隔为 $\Delta=2\delta$ 的"窗口"要铺下去。具体来说,如果我们有 $K$ 个中心 ${v_1,\dots,v_K}$,并且它们相邻之间都相差 $\Delta$,那么最左边中心的"覆盖下限"是 $$ v_1 - \delta = v_1 - \tfrac{\Delta}{2}, $$ 最右边中心的"覆盖上限"是 $$ v_K + \delta = v_K + \tfrac{\Delta}{2}. $$ 要保证所有 $x \in [X_{\min},,X_{\max}]$ 都能落进这串"区间并覆盖",只要 $$ v_1 - \tfrac{\Delta}{2} \le X_{\min}, \quad v_K + \tfrac{\Delta}{2} \ge X_{\max}. $$ 下面我们令最左侧的中心刚好满足 $v_1 - \tfrac{\Delta}{2} = X_{\min}$,于是 $$ v_1 = X_{\min} + \frac{\Delta}{2} = X_{\min} + \delta. $$ 如果相邻中心间距都为 $\Delta$,那么第 $m$ 个中心就是 $$ v_m = v_1 + (m-1)\,\Delta = \bigl(X_{\min} + \delta\bigr) + (m-1)\cdot(2\delta). $$ 此时,第 $K$ 个中心为 $$ v_K = X_{\min} + \delta + (K-1)\,2\delta = X_{\min} + (2K - 1)\,\delta. $$ 要满足"第 $K$ 个中心加上 $\delta$ 能覆盖到 $X_{\max}$",即 $$ v_K + \delta = X_{\min} + (2K - 1)\,\delta + \delta = X_{\min} + 2K\,\delta \ge X_{\max}. $$ 因此 $$ 2\,K\,\delta \ge X_{\max} - X_{\min} \Longleftrightarrow K \ge \frac{X_{\max} - X_{\min}}{2\,\delta}. $$ 我们取最小的整数满足这个不等式: $$ \boxed{K = \Bigl\lceil \frac{X_{\max} - X_{\min}}{2\,\delta} \Bigr\rceil.} $$ 3.得到码本 ${v_m}$ 和阈值 令 $$ \Delta = 2,\delta,\quad K = \Bigl\lceil \tfrac{X_{\max} - X_{\min}}{\Delta} \Bigr\rceil. $$ 对 $m = 1,2,\dots,K$,令 $$ v_m = X_{\min} + \delta + (m - 1),\Delta = X_{\min} + \delta + (m - 1),(2,\delta) = X_{\min} + \bigl(2m - 1\bigr),\delta. $$ 此时,第 1 个中心是 $v_1 = X_{\min} + \delta$; 第 $K$ 个中心是 $v_K = X_{\min} + (2K - 1),\delta$。 中点阈值数组 ${,t_m\mid m=1,\dots,K-1}$ 就是 $$ t_m = \frac{v_m + v_{m+1}}{2} = \frac{\bigl[X_{\min} + (2m-1)\delta\bigr] + \bigl[X_{\min} + (2(m+1)-1)\delta\bigr]}{2} = X_{\min} + 2m,\delta. $$ 因此,决策规则是: 如果 $x < t_1 = X_{\min} + 2\delta$,就量化到 $v_1 = X_{\min} + \delta$; 如果 $t_{m-1} \le x < t_m$ (即 $X_{\min} + 2(m-1)\delta \le x < X_{\min} + 2m\delta$),就量化到 $$ v_m = X_{\min} + (2m-1),\delta; $$ 如果 $x \ge t_{K-1} = X_{\min} + 2(K-1)\delta$,就量化到 $$ v_K = X_{\min} + (2K-1),\delta. $$ 维数选择推导 $$ \frac{1}{2}\left(\sum_{m=1}^r |\Delta \lambda_m| + \sum_{m=\kappa+1}^r |\widetilde\lambda_m|\right)\leq 量化阈值\tau $$ 已知 $\sum_{m=1}^r |\Delta \lambda_m|=\eta$ ,量化阈值 $\tau$ ,令 $s=2\tau-\eta$ ,设截断谱分解误差为 $\epsilon$ $$ \epsilon \leq 2\tau-\eta $$ 借助**前缀和+二分查找**寻找最小的 $\kappa$ : 预处理:计算前缀和 设绝对值降序的估计特征值列表为 $\bigl|\widetilde\lambda_1\bigr| \ge \bigl|\widetilde\lambda_2\bigr| \ge \cdots \ge \bigl|\widetilde\lambda_r\bigr|$, 计算它们的前缀和: $$ S_k = \sum_{m=1}^k \bigl|\widetilde\lambda_m\bigr|, \quad k=0,1,\dots,r, \quad S_0=0. $$ 总和 $S_r = \sum_{m=1}^r \bigl|\widetilde\lambda_m\bigr|$ 对应 $\kappa=0$ 时的最大截断误差。 将「尾和 ≤ 预算 $s$」转成前缀和条件 记预算 $s = 2\tau - \eta$,尾部累积(截断误差)为: $$ T(k) = \sum_{m=k+1}^r \bigl|\widetilde\lambda_m\bigr| = S_r - S_k. $$ 条件 $T(k) \le s$ 等价于: $$ S_k \ge S_r - s. $$ 定义阈值: $$ \theta = S_r - s. $$ 二分查找最小 $\kappa$ 在已排好序的数组 $S_k$(严格单调递增或非减)中,用二分查找找出最小的 $k$ 使得 $$ S_k ;\ge;\theta. $$ 此 $k$ 即为所求的最小截断秩 $\kappa$。 伪代码: # 输入:绝对值降序的估计特征值列表 |λ̃₁| ≥ |λ̃₂| ≥ ... ≥ |λ̃ᵣ|, 预算 s ≥ 0 S = [0] * (r+1) # 前缀和数组初始化 for k in range(1, r+1): S[k] = S[k-1] + |λ̃ₖ| # 计算前缀和(实际代码中需替换 |λ̃ₖ| 为具体变量) θ = S[r] - s # 计算查找阈值 # 在 S[0..r] 中二分查找最小 k 使得 S[k] ≥ θ low, high = 0, r while low < high: mid = (low + high) // 2 if S[mid] < θ: low = mid + 1 else: high = mid κ = low # 最终得到的最小截断 最近邻量化 算法思想: 已知量化级别 先假定你已有 $K$ 个量化级别(或簇中心): $$ v_1 < v_2 < \cdots < v_K $$ 这些值通常是事先人为设定好的、或者某种优化后得到的最佳码本。 计算决策边界(中点阈值) 对于一维空间,相邻两个量化级别之间的“分界”很自然就是它们的中点: $$ t_m = \frac{v_m + v_{m+1}}{2}, \quad m = 1,\dots,K-1 $$ 也就是一旦输入 $x$ 落在 $[t_{m-1},,t_m)$ 这个区间(约定 $t_0 = -\infty$,$t_K = +\infty$),就映为 $v_m$。 最近邻映射 对于矩阵 $M$ 中的每个元素 $x = M_{ij}$,只要找到它对应的区间号 $m$,就把 $x$ 替换为 $v_m$。 由于阈值已经按增序排列,查找时可以用二分搜索(binary search),使得整体复杂度为 $O(\log K)$/次映射。 误差保证 为了确保“重构误差”不会把重构值落到相邻簇去,需要保证真实误差 $\lvert A_{\kappa}(i,j) - M_{ij} \rvert$ 严格小于任意两个相邻量化级别之间的半距 $\frac{d_{\min}}{2}$。 这里 $d_m = v_{m+1} - v_m$,取最小间距 $d_{\min} = \min_{m} d_m$,就有误差阈值 $\tau = \frac{d_{\min}}{2}$。 输入:数组 v[1..K](升序排列) 输出:阈值数组 t[1..(K-1)](对应各区间的分界) function initialize_thresholds(v[1..K]): for m = 1 to K-1 do t[m] = ( v[m] + v[m+1] ) / 2 end for return t # 长度为 K-1 输入:矩阵 M (尺寸 N×N),量化级别 v[1..K],阈值数组 t[1..(K-1)] 输出:量化后矩阵 Ḿ(N×N) function quantizeMatrix(M, v, t): let N = number_of_rows(M) # 假设 M 是正方阵 N×N 创建新矩阵 Ḿ,大きさ N×N for i = 1 to N do for j = 1 to N do x = M[i][j] # 在阈值数组 t 中二分查找,找到第一个 t[m] 使得 x < t[m] # 如果没有找到(即 x >= t[K-1]),则归到最后一个簇 idx = binarySearchThreshold(t, x) # binarySearchThreshold 的返回: # 如果 x < t[1],返回 idx = 1; # 如果 t[m-1] <= x < t[m],返回 idx = m; # 如果 x >= t[K-1],返回 idx = K。 Ḿ[i][j] = v[idx] end for end for return Ḿ 如果量化阈值不一样,你需要预先知道矩阵的某个元素$|(A - A_\kappa){ij}|$的真实值属于哪个类别(1、4、10),以给元素误差不同的量化阈值,而不是默认的 $d{\min}$ $$ \text{误差矩阵: } A - A_\kappa = \left( A - \widetilde{A} \right) + \left( \widetilde{A} - A_\kappa \right) = \sum_{m=1}^r \Delta \lambda_m x_m x_m^T + \sum_{m=\kappa+1}^r \widetilde{\lambda}_m x_m x_m^T $$ 而且不能进行放缩: $$ |(A - A_\kappa)_{ij}| \leq \frac{1}{2}\left(\sum_{m=1}^r |\Delta \lambda_m| + \sum_{m=\kappa+1}^r |\widetilde\lambda_m|\right) $$ 这个式子会导致误差矩阵每个位置的元素取了同样的上界(即都有同样的误差),不能体现真实的误差。 方法一: 实验法,选取若干组数据做实验, 数据包括真实矩阵 $A$ 和重构矩阵 $A_\kappa$ ,选取不同的 $\kappa$ 值计算误差矩阵 $E(\kappa)$ ,根据真实矩阵A,预先知道$E(\kappa)$ 每个位置的量化阈值,若每个位置$E(\kappa)$ 都小于其的量化阈值,那么当前的 $\kappa$ 符合条件。 方法二: $$ |(A - A_\kappa)_{ij}| \leq \frac{1}{2}\left(\sum_{m=1}^r |\Delta \lambda_m| + \sum_{m=\kappa+1}^r |\widetilde\lambda_m|\right) \leq \min_{1\le m\le K-1} d_m. $$ 滤波误差: 高飞、李镇和都是实验统计误差加和获取 + 幂律分布和线性分布 精度预估?? 量化的间隔是不是就和分布有关,有无其他影响因素。 通信原理,采样量化。 压缩感知的话量化分隔不是均匀的。 假设都是破松分布 估计带权邻接矩阵(存在量化误差),比较分布式算法的误差。
科研
zy123
5月10日
0
7
0
2025-05-05
动态图神经网络
动态图神经网络 如何对GAT的权重($W$)和注意力参数($a$)进行增量更新(邻居偶尔变化) 1. 核心思想 局部更新:邻居变化的节点及其直接邻域的权重和注意力参数需要调整,其他部分冻结。 梯度隔离:反向传播时,仅计算受影响节点的梯度,避免全局参数震荡。 2. 数学实现步骤 (1) 识别受影响的节点 设邻居变化后的新邻接矩阵为 $\tilde{A}$,原邻接矩阵为 $A$,受影响节点集合 $\mathcal{V}_{\text{affected}}$ 包括: 新增或删除边的两端节点(直接受影响)。 这些节点的1-hop邻居(间接受影响,根据GAT层数决定)。 (2) 损失函数局部化 仅对 $\mathcal{V}_{\text{affected}}$ 中的节点计算损失: $$ \mathcal{L}_{\text{incremental}} = \sum_{i \in \mathcal{V}_{\text{affected}}} \ell(y_i, \hat{y}_i) $$ 其中 $\ell$ 为交叉熵损失,$y_i$ 为标签,$\hat{y}_i$ 为模型输出。 (3) 梯度计算与参数更新 梯度掩码: 反向传播时,非受影响节点的梯度强制置零: $$ \nabla_{W,a} \mathcal{L}{\text{incremental}} = \left{ \begin{array}{ll} \nabla{W,a} \ell(y_i, \hat{y}i) & \text{if } i \in \mathcal{V}{\text{affected}} \ 0 & \text{otherwise} \end{array} \right. $$ 参数更新: 使用优化器(如Adam)仅更新有梯度的参数: $$ W \leftarrow W - \eta \nabla_W \mathcal{L}{\text{incremental}}, \quad a \leftarrow a - \eta \nabla_a \mathcal{L}{\text{incremental}} $$ 其中 $\eta$ 为较小的学习率(防止过拟合)。 (4) 注意力权重的动态适应 GAT的注意力机制会自动适应新邻居: $$ \alpha_{ij} = \text{softmax}\left(\text{LeakyReLU}\left(a^T [W h_i \| W h_j]\right)\right) $$ 由于 $W$ 和 $a$ 已局部更新,新邻居 $j \in \tilde{\mathcal{N}}(i)$ 的权重 $\alpha_{ij}$ 会重新计算。 3. 适用场景 低频变化:如社交网络每天新增少量边、论文引用网络月度更新。 局部变化:每次变化仅影响图中少量节点(<10%)。 若邻居高频变化(如秒级更新),需改用动态GNN(如TGAT)或时间序列建模。 EvolveGCN EvolveGCN-H 1. EvolveGCN-H核心思想 EvolveGCN-H 通过 GRU(门控循环单元) 动态更新 GCN 每一层的权重矩阵 $W_t^{(l)}$,将权重矩阵视为 GRU 的 隐藏状态,并利用当前时间步的 节点嵌入(特征) 作为输入来驱动演化。 关键特点: 输入依赖:利用节点嵌入 $H_t^{(l)}$ 指导权重更新。 时序建模:通过 GRU 隐式捕捉参数演化的长期依赖。 2. 动态更新流程(以第 $l$ 层为例) 输入: 当前节点嵌入矩阵 $H_t^{(l)} \in \mathbb{R}^{n \times d}$: 上一时间步的权重矩阵 $W_{t-1}^{(l)} \in \mathbb{R}^{d \times d'}$: 邻接矩阵 $A_t \in \mathbb{R}^{n \times n}$: 输出: 更新后的权重矩阵 $W_t^{(l)} \in \mathbb{R}^{d \times d'}$。 下一层节点嵌入 $H_t^{(l+1)} \in \mathbb{R}^{n \times d'}$。 3. 动态更新示意图 Time Step t-1 Time Step t +-------------------+ +-------------------+ | Weight Matrix | | Weight Matrix | | W_{t-1}^{(l)} | --(GRU更新)--> | W_t^{(l)} | +-------------------+ +-------------------+ ^ ^ | | +-------------------+ +-------------------+ | Node Embeddings | | Node Embeddings | | H_t^{(l)} | --(GCN计算)--> | H_t^{(l+1)} | +-------------------+ +-------------------+ ^ ^ | | +-------------------+ +-------------------+ | 邻接矩阵 A_t | | 邻接矩阵 A_{t+1} | | (显式输入) | | (下一时间步输入) | +-------------------+ +-------------------+ $$ \begin{align*} W_t^{(l)} &
科研
zy123
5月5日
0
5
0
2025-04-26
zy
如何确定kmeans的簇数?节点之间的流量,空间转为时间的图。 压缩感知 函数拟合 采样定理 傅里叶变换 流量单位时间内的流量 若有中心服务器,可以保存全局0/1邻接矩阵A+带权邻接矩阵+特征矩阵H,周围节点通信一次即可获取全局信息 无中心服务器,每个节点可以获取0/1邻接矩阵A+带权邻接矩阵 实时重构出邻接矩阵a和权重矩阵 是否有帮助? 带权邻接矩阵-》特征矩阵? TGAT或EvolveGCN进行推理? 带权邻接矩阵仅作为边特征,特征矩阵? 社交网络中邻居关系,节点特征是不断变化的,可以利用TGAT或EvolveGCN进行预测,那么就要用已有训练集。但是不适合仿真使用,仿真是基于节点移动模型的。 关键假设:假设历史真实数据已知 可以拟合 二次函数 当作当前的测量值 因为我们要做实时估计 可能来不及获取实时值 但可以拟合过去的 或者直接谱分解上一个时刻重构的矩阵,得到特征值和特征向量序列 加上随机扰动作为观测输入 证明特征值稳定性: 网络平均度+高飞证明+gpt+实验。 需要解决的问题:确定Kmeans簇数、选取的特征值特征向量的维数。 先研究0-1矩阵 我现在有一个真实对称矩阵A,只有0-1元素,我对它的特征值和特征向量进行估计,可以得到n个特征值和特征向量,重构出 $\widetilde{A}$,但是我只选择了前r个特征值和特征向量进行谱分解重构,可以得到A_r,最后我对A_r使用kmeans量化的方法,得到A_final簇数为2,怎么进行误差分析,确定我这里的r,使得我最终得到的A_final可以满足精确重构A的要求。 这里可以假定得到n个特征值和特征向量这里的误差为\eta 特征值误差分析(方差)直接看李振河的,滤波误差看郭款 $A-\tilde A$这里是滤波误差,是否包括特征值误差和特征向量误差?
科研
zy123
4月26日
0
3
0
2025-04-26
Mesa仿真
Mesa仿真 配置环境 requirements.txt mesa[rec] # 包含 networkx、matplotlib、ipywidgets、solara 等推荐依赖 jupyterlab numpy pandas Conda 命令行 # 1) 添加 conda-forge 通道并设为最高优先级 conda config --add channels conda-forge conda config --set channel_priority strict # 2) 创建并激活新环境(这里以 python 3.11 为例) conda create -n mesa-env python=3.11 -y conda activate mesa-env # 3a) 通过 pip 安装(使用上面的 requirements.txt) pip install -r requirements.txt # 或者 3b) 纯 Conda 安装等价包(推荐所有包都从 conda-forge) conda install \ mesa=3.1.5 networkx matplotlib ipywidgets solara \ numpy pandas jupyterlab \ -c conda-forge
科研
zy123
4月26日
0
3
0
上一页
1
2
3
...
11
下一页