如何在 JAX 中实现支持动态形状的 next 函数(字符串重写系统)
发布时间 - 2026-01-03 00:00:00 点击率:次本文详解如何在 jax 中绕过动态形状限制,用静态形状语义实现可向量化、可递归应用的规则替换函数,适用于字符串重写系统等场景。
JAX 的核心约束之一是:所有变换(如 vmap、jit、grad)要求中间和输出数组具有静态形状(static shape)——即形状不能依赖于运行时张量值。而原始 replace_first_one 函数中,jnp.where(arr == 1)[0] 的长度、jnp.concatenate 的结果长度均随输入数据动态变化,导致 vmap 报错:size argument of jnp.nonzero must be statically specified。
要使函数兼容 vmap,关键在于消除所有动态形状分支,转而采用“填充-掩码-切片”范式,确保每一步输出尺寸完全可推导且恒定。以下是完整、可向量化、可嵌套调用的实现方案:
✅ 正确实现(静态形状兼容版)
import jax
import jax.numpy as jnp
from jax import vmap
# 所有规则必须统一长度(静态形状前提)
rules_int = jnp.array([
[0, 0], # rule 0 → length=2
[1, 1], # rule 1 → length=2
], dtype=jnp.int32)
def replace_first_one(arr, action):
"""
静态形状安全的首 '1' 替换函数:
- 输入 arr 形状固定为 (L,),action 为标量
- 输出形状固定为 (L + 1),因每次替换:删1个元素 + 插入2个 → 净增1
- 使用 jnp.where(..., size=1) 强制返回固定长度索引
- 使用 dynamic_update_slice 避免 concat 动态拼接
"""
L = arr.shape[0]
# 查找第一个 1 的位置;若不存在,index = L(越界,后续逻辑自动跳过替换)
indices = jnp.where(arr == 1, size=1, fill_value=L)[0]
index = indices[0] # scalar index, static at compile time
# 待插入规则向量
rule = rules_int[action]
# 预分配输出数组(长度 = L - 1 + len(rule) = L + 1)
output = jnp.zeros(L + 1, dtype=arr.dtype)
# 构造插入前段:arr[:index]
pre = jnp.where(jnp.arange(L) < index, arr, 0)
# 构造插入后段:arr[index+1:]
post = jnp.where(jnp.arange(L) > index, arr, 0)
# 拼接三段(逻辑上)→ 但实际用 dynamic_update_slice 实现高效写入
# 更简洁做法:先填入 arr[0:index] 和 arr[index+1:],再覆盖插入 rule
# 我们分步构造:
output = output.at[:index].set(arr[:index])
output = output.at[index:index+2].set(rule) # rule 长度固定为 2
output = output.at[index+2:].set(arr[index+1:L-1]) # 注意对齐长度
# ⚠️ 上述 set 可能越界,更鲁棒写法:使用 dynamic_update_slice + mask
# 推荐最终版本(零拷贝、边界安全):
output = jnp.zeros(L + 1, dtype=arr.dtype)
# 写入前段 [0:index]
output = jax.lax.dynamic_update_slice(output, arr[:index], (0,))
# 写入规则 [index:index+2]
output = jax.lax.dynamic_update_slice(output, rule, (index,))
# 写入后段 [index+2:]
tail_start = index + 1 # 原 arr 中跳过 index 后的起始位置
tail_len = L - tail_start
pad_len = (L + 1) - (index + 2) - tail_len
padded_tail = jnp.pad(arr[tail_start:], (0, pad_len), constant_values=0)
output = jax.lax.dynamic_update_slice(output, padded_tail, (index + 2,))
return output
# ✅ 现在可安全 vmap
batch_arr = jnp.array([
[1, 4, 5, 1], # → 替换第0个1 → [0,0,4,5,1]
[6, 1, 8, 1], # → 替换第1个1 → [6,1,1,8,1]
])
batch_actions = jnp.array([0, 1])
vectorized_replace = vmap(replace_first_one, in_axes=(0, 0))
result = vectorized_replace(batch_arr, batch_actions)
print(result)
# [[0 0 4 5 1]
# [6 1 1 8 1]]? 支持递归应用(字符串重写系统)
若需反复应用规则直至无 1 可替换(即模拟图灵机或 L-system),可用 jax.lax.while_loop 实现静态迭代上限下的循环:
def rewrite_until_stable(init_arr, max_steps=10):
def cond_fn(state):
arr, step = state
has_one = jnp.any(arr == 1)
return jnp.logical_and(has_one, step < max_steps)
def body_fn(state):
arr, step = state
# 找到首个 1 对应的 action(示例:固定用 rule 0;实际可查表)
index = jnp.where(arr == 1, size=1, fill_value=arr.shape[0])[0][0]
action = jnp.where(index < arr.shape[0], 0, 0) # placeholder
new_arr = replace_first_one(arr, action)
return new_arr, step + 1
final_arr, _ = jax.lax.while_loop(cond_fn, body_fn, (init_arr, 0))
return final_arr
# 示例:jnp.array([1]) → [0,0] → 无1 → 停止
stable = rewrite_until_
stable(jnp.array([1]))
print(stable) # [0 0]⚠️ 注意事项与权衡
- 规则长度必须统一:这是静态形状的硬性要求。若原始规则长度不一(如 [0,0] vs [1,1,1]),需补零或截断至最大长度,并用 mask 控制有效区域。
- 性能提示:dynamic_update_slice 比 concatenate 更适合 JIT;避免 jnp.where 无 size 参数的用法。
-
替代方案:若业务逻辑必须支持真正变长输出(如生成不同长度 token 序列),则应考虑:
- 在 Python 层循环(放弃 vmap 加速);
- 使用 jax.vmap + jax.pmap 分 batch 处理,每 batch 内部统一 padding;
- 迁移至支持 ragged tensor 的框架(如 TensorFlow with tf.RaggedTensor),但将失去 JAX 生态优势。
总之,JAX 中的“动态形状”并非不可逾越,而是需要以静态契约重构逻辑——通过预分配、填充、掩码与 slice 操作,在编译期锁定维度,从而释放 vmap/jit 的全部潜力。
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
Laravel如何编写单元测试和功能测试?(PHPUnit示例)
制作网站软件推荐手机版,如何制作属于自己的手机网站app应用?
如何在建站主机中优化服务器配置?
如何在香港免费服务器上快速搭建网站?
成都品牌网站制作公司,成都营业执照年报网上怎么办理?
如何在HTML表单中获取用户输入并用JavaScript动态控制复利计算循环
如何用AI帮你把自己的生活经历写成一个有趣的故事?
Win11怎么查看显卡温度 Win11任务管理器查看GPU温度【技巧】
Laravel路由怎么定义_Laravel核心路由系统完全入门指南
javascript中的try catch异常捕获机制用法分析
打开php文件提示内存不足_怎么调整php内存限制【解决方案】
宙斯浏览器怎么屏蔽图片浏览 节省手机流量使用设置方法
JS经典正则表达式笔试题汇总
Laravel如何实现文件上传和存储?(本地与S3配置)
在centOS 7安装mysql 5.7的详细教程
如何在IIS中新建站点并配置端口与物理路径?
高性价比服务器租赁——企业级配置与24小时运维服务
javascript如何操作浏览器历史记录_怎样实现无刷新导航
*服务器网站为何频现安全漏洞?
如何在万网利用已有域名快速建站?
Android GridView 滑动条设置一直显示状态(推荐)
Laravel Livewire是什么_使用Laravel Livewire构建动态前端界面
Java遍历集合的三种方式
如何在阿里云部署织梦网站?
Laravel API资源类怎么用_Laravel API Resource数据转换
网站制作壁纸教程视频,电脑壁纸网站?
在Oracle关闭情况下如何修改spfile的参数
北京专业网站制作设计师招聘,北京白云观官方网站?
laravel怎么为API路由添加签名中间件保护_laravel API路由签名中间件保护方法
,在苏州找工作,上哪个网站比较好?
Laravel如何安装使用Debugbar工具栏_Laravel性能调试与SQL监控插件【步骤】
东莞专业网站制作公司有哪些,东莞招聘网站哪个好?
如何快速搭建高效香港服务器网站?
高性能网站服务器配置指南:安全稳定与高效建站核心方案
Laravel PHP版本要求一览_Laravel各版本环境要求对照
Laravel的契約(Contracts)是什么_深入理解Laravel Contracts与依赖倒置
PHP正则匹配日期和时间(时间戳转换)的实例代码
Laravel如何正确地在控制器和模型之间分配逻辑_Laravel代码职责分离与架构建议
Android中Textview和图片同行显示(文字超出用省略号,图片自动靠右边)
Laravel如何发送系统通知?(Notification渠道示例)
php静态变量怎么调试_php静态变量作用域调试技巧【解答】
Laravel表单请求验证类怎么用_Laravel Form Request分离验证逻辑教程
iOS UIView常见属性方法小结
香港服务器选型指南:免备案配置与高效建站方案解析
个人摄影网站制作流程,摄影爱好者都去什么网站?
JS去除重复并统计数量的实现方法
Laravel事件监听器怎么写_Laravel Event和Listener使用教程
Win10如何卸载预装Edge扩展_Win10卸载Edge扩展教程【方法】
如何在景安服务器上快速搭建个人网站?
合肥制作网站的公司有哪些,合肥聚美网络科技有限公司介绍?


stable(jnp.array([1]))
print(stable) # [0 0]