# ***************************************************************
# Copyright (c) 2023 Jittor. All Rights Reserved.
# Maintainers:
# Dun Liang <randonlang@gmail.com>.
# Wenyang Zhou <576825820@qq.com>
# Guoye Yang <498731903@qq.com>
#
# This file is subject to the terms and conditions defined in
# file 'LICENSE.txt', which is part of this source code package.
# ***************************************************************
import jittor as jt
import numpy as np
import math
from collections.abc import Sequence,Iterable
[文档]
def knn(unknown, known, k):
'''
对未知向量寻找与已知向量里中的欧氏距离最近的 ``k`` 个向量
参数:
- unknown (Var): 形状 ``(b, n, c)`` ,其中 ``b`` 是批次数, ``n`` 是每批次未知向量个数, ``c`` 是每个向量维数
- known (Var): 形状 ``(b, m, c)`` ,其中 ``b`` 和 ``c`` 上同, ``m`` 是每批次已知向量个数
- k (int): 对于每个未知向量寻找多少个最近向量
返回值:
- ``tuple[Var, Var]`` ,其中第一个元素是形状 ``(b, n, k)`` 表示对每个未知向量,在当前批次中找到的最近 ``k`` 个已知向量的欧式距离的平方,第二个元素是形状 ``(b, n, k)`` 表示找到最近向量的下标
代码示例:
>>> unknown = jt.randn(1, 4, 3)
>>> known = jt.randn(1, 10, 3)
>>> jt.misc.knn(unknown, known, 2)
(jt.Var([[[0.04125667 1.0006377 ]
[1.2794693 2.05327 ]
[1.4255599 3.4547305 ]
[0.849751 1.5978208 ]]], dtype=float32),
jt.Var([[[8 4]
[9 4]
[4 8]
[1 3]]], dtype=int32))
这个函数只在 ``c`` 为 3 的时候正确工作。
'''
b, n, c = unknown.shape
_, m, _ = known.shape
dists2 = jt.empty((b, n, k), dtype="float")
idx = jt.empty((b, n, k), dtype="int")
src = '''
__inline_static__
@python.jittor.auto_parallel(2, block_num=256)
void knn_kernel(int b, int batch_index, int n, int index, int m,
const float *__restrict__ unknown,
const float *__restrict__ known,
float *__restrict__ dist2,
int *__restrict__ idx) {
#define K %s
unknown += batch_index * n * 3;
known += batch_index * m * 3;
dist2 += batch_index * n * K;
idx += batch_index * n * K;
int j = index;
{
float ux = unknown[j * 3 + 0];
float uy = unknown[j * 3 + 1];
float uz = unknown[j * 3 + 2];
float tmp_dist[K];
int tmp_idx[K];
#pragma unroll
for (int i=0; i<K; i++) tmp_dist[i] = 1e30;
for (int k = 0; k < m; ++k) {
float x = known[k * 3 + 0];
float y = known[k * 3 + 1];
float z = known[k * 3 + 2];
float d = (ux - x) * (ux - x) + (uy - y) * (uy - y) + (uz - z) * (uz - z);
int first = -1;
#pragma unroll
for (int i=0; i<K; i++)
if (first == -1 && d<tmp_dist[i])
first = i;
if (first == -1) continue;
#pragma unroll
for (int i=0; i<K; i++)
if (K-1-i > first) {
tmp_dist[K-1-i] = tmp_dist[K-2-i];
tmp_idx[K-1-i] = tmp_idx[K-2-i];
}
tmp_dist[first] = d;
tmp_idx[first] = k;
}
#pragma unroll
for (int i=0; i<K; i++) {
dist2[j * K + i] = tmp_dist[i];
idx[j * K + i] = tmp_idx[i];
}
}
}
knn_kernel(in0->shape[0], 0, in0->shape[1], 0, in1->shape[1], in0_p, in1_p, out0_p, out1_p);
''' % k
return jt.code([unknown, known], [dists2, idx],
cpu_src=src,
cuda_src=src)
[文档]
def index_add_(x, dim, index, tensor):
'''
对于 ``index`` 中的每个下标和 ``tensor`` 里对应的每个张量,在 ``x`` 的 ``dim`` 维对应的下标处加上这个张量
参数:
- x (Var): 形状 ``(..., k, ...)``
- dim (int): 上面 ``x`` 中 ``k`` 对应的是第几维
- index (Var): 形状 ``(n)`` 选取要加上张量的下标
- tensor (Var): 形状 ``(n, ..., ...)`` 每个下标处要加上的张量
代码示例:
>>> x = jt.ones(2, 3, 4)
>>> jt.index_add_(x, 0, jt.array([0, 1]), jt.array([jt.ones(3, 4), jt.ones(3, 4) * 2]))
>>> jt.index_add_(x, 1, jt.array([0, 1]), jt.array([jt.ones(2, 4), jt.ones(2, 4) * 2]))
>>> x
jt.Var([[[3. 3. 3. 3.]
[3. 3. 3. 3.]
[2. 2. 2. 2.]]
[[5. 5. 5. 5.]
[5. 5. 5. 5.]
[3. 3. 3. 3.]]], dtype=float32)
'''
assert len(index.shape) == 1
assert tensor.shape[0] == index.shape[0]
x[(slice(None,),)*dim+(index,)] += tensor
jt.Var.index_add_ = index_add_
def __copy__(x):
return x.copy().detach()
jt.Var.__copy__ = __copy__
def __deepcopy__(x,memo):
result = x.copy().detach()
memo[id(x)]=result
return result
jt.Var.__deepcopy__ = __deepcopy__
def __len__(x):
return x.shape[0]
jt.Var.__len__ = __len__
def __iter__(x):
result = []
for i in range(x.shape[0]):
result.append(x[i])
return result.__iter__()
jt.Var.__iter__ = __iter__
def __contains__(x, key):
return bool((x == key).any())
jt.Var.__contains__ = __contains__
[文档]
def new(x, *args):
'''
创建一个和 ``x`` 类型相同的张量
参数:
- x (``Var``): 一个张量
- a (``Var``): 待转换类型的张量
或者
- n1, n2, ... (``int``): 新张量每维的长度
返回值:
如果给的参数是一个张量,则将其转换为和 ``x`` 类型相同的张量;否则新创建一个给定形状,类型与 ``x`` 相同的未初始化张量
代码示例:
>>> x = jt.array([1.0, 2.0, 3.0])
>>> x
jt.Var([1. 2. 3.], dtype=float32)
>>> x.new(3, 4)
jt.Var([[-0.62324923 0.81981313 0.49348482 -0.80826735]
[-2.185655 -0.12619412 -0.36160922 -0.28803992]
[-1.3812838 -0.3991161 0.9999802 -0.11363947]], dtype=float32)
>>> a = jt.array([4, 5])
>>> a
jt.Var([4 5], dtype=int32)
>>> x.new(a)
jt.Var([4. 5.], dtype=float32)
'''
if len(args) != 1 or isinstance(args[0], int):
return jt.empty(args, x.dtype)
return jt.array(args[0]).cast(x.dtype)
jt.Var.new = new
def __index__(x):
return int(x.item())
jt.Var.__index__ = __index__
[文档]
def sort(input, dim=-1, descending=False, stable=False):
'''
对输入张量 ``input`` 沿着指定维按升序非稳定排序。如果不给定 ``dim`` ,则默认为输入的最后一维。如果指定参数 ``descending`` 为 ``True`` ,则按降序排序。如果指定参数``stable`` 为 ``True`` ,则为稳定排序。
参数:
- input (``Var``): 要对比的张量
- dim (``int``, 可选): 沿着此维排序,默认为最后一维
- descending (``bool``, 可选): 布尔值,控制升降排序,默认为升序
- stable(``bool``, 可选): 布尔值,控制稳定排序,默认非稳定排序
返回值:
返回元组 ``(value, index)`` , ``index`` 为原始输入中的下标。
代码示例:
>>> x = jt.random((3, 4))
jt.Var([[ 0.25843996 0.6806731 0.48188502 -0.8385996 ]
[-0.97904104 -1.0399561 -0.97162217 3.834338 ]
[-0.6144879 -0.97539455 -0.81440794 -0.12566419]], dtype=float32)
>>> value, index = jt.sort(x)
>>> value
jt.Var([[-0.8385996 0.25843996 0.48188502 0.6806731 ]
[-1.0399561 -0.97904104 -0.97162217 3.834338 ]
[-0.97539455 -0.81440794 -0.6144879 -0.12566419]], dtype=float32)
>>> index
jt.Var([[3 0 2 1]
[1 0 2 3]
[1 2 0 3]], dtype=int32)
'''
index, value = jt.argsort(input, dim, descending)
return value, index
jt.Var.sort = sort
[文档]
def all(x, dim=()):
'''
在指定维度上计算逻辑与
参数:
- x (Var): 被计算逻辑与的张量
- dim (int | tuple[int, ...]): 计算逻辑与的维度,默认为全部维度,即全部元素逻辑与得到一个元素
返回值:
计算逻辑与的结果,形状为 ``x`` 去掉 ``dim`` 所选维度,元素类型为 ``bool``
代码示例:
>>> x = jt.randn(2, 3) > 0
>>> x
jt.Var([[ True True False]
[ True False True]], dtype=bool)
>>> jt.all(x)
jt.Var([False], dtype=bool)
>>> jt.all(x, 0)
jt.Var([ True False False], dtype=bool)
>>> jt.all(x, 1)
jt.Var([False False], dtype=bool)
'''
return x.all_(dim).bool()
jt.Var.all = all
[文档]
def any(x,dim=()):
'''
在指定维度上计算逻辑或
参数:
- x (Var): 被计算逻辑或的张量
- dim (int | tuple[int, ...]): 计算逻辑或的维度,默认为全部维度,即全部元素逻辑或得到一个元素
返回值:
计算逻辑或的结果,形状为 ``x`` 去掉 ``dim`` 所选维度,元素类型为 ``bool``
代码示例:
>>> x = jt.randn(2, 3) > 0
>>> x
jt.Var([[ True False True]
[False False True]], dtype=bool)
>>> jt.any(x)
jt.Var([ True], dtype=bool)
>>> jt.any(x, 0)
jt.Var([ True False True], dtype=bool)
>>> jt.any(x, 1)
jt.Var([ True True], dtype=bool)
'''
return x.any_(dim).bool()
jt.Var.any = any
[文档]
def bernoulli(input):
'''
对于每个元素 ``p`` ,返回概率为 ``p`` 的伯努利分布的随机值:
.. math::
\\begin{cases}
1 & \\text{ with probability } p \\\\
0 & \\text{ with probability } (1 - p)
\\end{cases}
参数:
- input (Var): 每个采样所用的概率 ``p``
返回值:
形状与数据类型和 ``input`` 一样的张量,原张量中每个元素 ``p`` 对应返回值里的元素,以概率 ``p`` 为 1,概率 ``1 - p`` 为 0
代码示例:
>>> x = jt.array([0.0, 0.5, 1.0])
>>> jt.bernoulli(x)
jt.Var([0. 0. 1.], dtype=float32)
>>> jt.bernoulli(x)
jt.Var([0. 0. 1.], dtype=float32)
>>> jt.bernoulli(x)
jt.Var([0. 1. 1.], dtype=float32)
'''
return (input>jt.rand_like(input)).cast(input.dtype)
[文档]
def repeat(x, *shape):
r'''
在指定维度上重复张量。
参数:
x (``Var``): 输入张量
shape (``tuple[int]``): 重复的次数,可以是一个整数,也可以是一个元组。如果是一个整数,则表示在所有维度上重复相同次数;如果是一个元组,则表示在每个维度上重复的次数。
代码示例:
>>> x = jt.array([1, 2, 3])
>>> x.repeat(4, 2)
jt.Var([[1 2 3 1 2 3]
[1 2 3 1 2 3]
[1 2 3 1 2 3]
[1 2 3 1 2 3]], dtype=int32)
>>> x.repeat(4, 2, 1).size()
[4, 2, 3,]
'''
if len(shape) == 1 and isinstance(shape[0], Sequence):
shape = shape[0]
len_x_shape = len(x.shape)
len_shape = len(shape)
x_shape = x.shape
rep_shape = shape
if len_x_shape < len_shape:
x_shape = (len_shape - len_x_shape) * [1] + x.shape
x = x.broadcast(x_shape)
elif len_x_shape > len_shape:
rep_shape = (len_x_shape - len_shape) * [1] + list(shape)
reshape_shape = []
broadcast_shape = []
for x_s,r_s in zip(x_shape,rep_shape):
if r_s != 1:
reshape_shape.append(1)
broadcast_shape.append(r_s)
reshape_shape.append(x_s)
broadcast_shape.append(1)
x = x.reshape(reshape_shape)
x = x.broadcast(broadcast_shape)
tar_shape = (np.array(x_shape) * np.array(rep_shape)).tolist()
x = x.reshape(tar_shape)
return x
jt.Var.repeat = repeat
# tile = jt.Var.tile = repeat
ne = jt.Var.ne = jt.Var.not_equal
[文档]
def repeat_interleave(x,repeats,dim=None):
'''
将张量在某个维度上每个元素重复多次
参数:
- x (``Var``): 被重复的张量,形状 :math:`(n_0, \\ldots, n_{\\mathtt{dim}}, \\ldots, n_k)`
- repeats (``int``): 每个元素重复的次数
- dim (``int``): 在哪个维度上重复,负数表示从后往前数,默认为将向量扁平化后重复
返回值:
``x`` 在 ``dim`` 维度每个元素重复 ``repeat`` 次得到的向量,形状 :math:`(n_0, \\ldots, n_{\\mathtt{dim}} \\times \\mathtt{repeats}, \\ldots, n_k)`
示例代码:
>>> x = jt.array([[1, 2], [3, 4]])
>>> x.repeat_interleave(2, 0)
jt.Var([[1 2]
[1 2]
[3 4]
[3 4]], dtype=int32)
>>> x.repeat_interleave(2, 1)
jt.Var([[1 1 2 2]
[3 3 4 4]], dtype=int32)
>>> x.repeat_interleave(2)
jt.Var([1 1 2 2 3 3 4 4], dtype=int32)
'''
assert isinstance(repeats,int)
if dim == None:
x = x.reshape(-1)
dim=0
if dim<0: dim+=x.ndim
tar_shape = list(x.shape)
x_shape = list(x.shape)
tar_shape[dim] = tar_shape[dim]*repeats
dims = []
for i in range(len(tar_shape)):
if dim==i:
dims.append(f"i{i}/{repeats}")
else:
dims.append(f"i{i}")
return x.reindex(tar_shape,dims)
jt.Var.repeat_interleave = repeat_interleave
[文档]
def chunk(x, chunks, dim=0):
'''
将张量在 ``dim`` 维度切分为 ``chunks`` 块
切块尽量均匀。如果 ``x`` 在 ``dim`` 维的长度不是 ``chunks`` 的整倍数,最后一个块的长度会小于之前块的长度。如果无法切分出 ``chunks`` 个非空的块,则会返回少于 ``chunks`` 个块。
参数:
- x (Var): 被切块的张量,形状 :math:`(n_0, \\ldots, n_{\\mathtt{dim}}, \\ldots, n_k)`
- chunks (int): 切成的块数
- dim (int, 可选): 在哪个维度切块,负数表示从后往前数。默认值:0
返回值:
``list[Var]`` 为切块后按顺序得到的各个块组成的列表。
示例代码:
>>> x = jt.arange(10)
>>> x.chunk(2)
[jt.Var([0 1 2 3 4], dtype=int32), jt.Var([5 6 7 8 9], dtype=int32)]
>>> x.chunk(4)
[jt.Var([0 1 2], dtype=int32),
jt.Var([3 4 5], dtype=int32),
jt.Var([6 7 8], dtype=int32),
jt.Var([9], dtype=int32)]
>>> x.chunk(6)
[jt.Var([0 1], dtype=int32),
jt.Var([2 3], dtype=int32),
jt.Var([4 5], dtype=int32),
jt.Var([6 7], dtype=int32),
jt.Var([8 9], dtype=int32)]
'''
if dim<0:
dim += x.ndim
l = x.shape[dim]
res = []
if l <= chunks:
for i in range(l):
res.append(x[(slice(None,),)*dim+([i,],)])
else:
nums = (l-1) // chunks + 1
for i in range(chunks-1):
res.append(x[(slice(None,),)*dim+(slice(i*nums,(i+1)*nums),)])
if (i+1)*nums < l:
res.append(x[(slice(None,),)*dim+(slice((i+1)*nums,None),)])
return res
jt.Var.chunk = chunk
[文档]
def expand(x, *shape):
'''
扩展并广播一个张量到指定形状。
指定的形状每一维必须为下列值之一:
- ``-1`` 表示不改变这维度的长度
- 和 ``x`` 对应维长度一样,表示不改变这个维度的长度
- 新的长度,要求 ``x`` 对应维长度为 1,表示在这个维度上扩展
参数:
- x (Var): 被扩展的张量
- shape (tuple[int, ...]): 扩展到的形状
或者
- x (Var): 被扩展的张量
- n0, n1, ... (int): 扩展到的形状
返回值:
扩展到指定形状的张量
示例代码:
>>> a = jt.zeros((3,1))
>>> a.expand(3, 4).shape
[3,4,]
>>> a.expand(-1, 4).shape
[3,4,]
'''
if len(shape) == 1 and isinstance(shape[0], (tuple,list,jt.NanoVector)):
shape = shape[0]
shape = list(shape)
for i in range(len(shape)):
if shape[i] == -1:
shape[i] = x.shape[i]
return x.broadcast(shape)
jt.Var.expand = expand
[文档]
def t(x):
'''
转置张量的最后两个维度
参数:
- x (``Var``): 被转置的张量,形状 :math:`(n_0, n_1, \\ldots, n_{k - 3}, n_{k - 2}, n_{k - 1})`
返回值:
转置后的张量,形状 :math:`(n_0, n_1, \\ldots, n_{k - 3}, n_{k - 1}, n_{k - 2})`
示例代码:
>>> x = jt.array([[1, 2], [3, 4]])
>>> x.t()
jt.Var([[1 3]
[2 4]], dtype=int32)
'''
pose = [i for i in range(x.ndim)]
pose[-1], pose[-2] = pose[-2], pose[-1]
return x.transpose(*pose)
jt.Var.t = t
# def median(x,dim=None,keepdim=False, keepdims=False):
# keepdim = keepdim or keepdims
# if dim is None:
# x = x.reshape(-1)
# dim=0
# _,x = jt.argsort(x, dim)
# slices = [slice(None) for i in range(dim-1)]
# k = (x.shape[dim]-1)//2
# if keepdim:
# slices.append(slice(k,k+1))
# else:
# slices.append(k)
# return x[tuple(slices)]
# jt.Var.median = median
[文档]
def stack(x, dim=0):
'''
将多个形状相同的张量组合成为一个更高维的张量
参数:
- x (``list[Var]``): 被组合的张量,每个形状相同,都是 :math:`(n_0, \\ldots, n_{\\mathtt{dim}}, \\ldots, n_k)`
- dim (``int``): 第几个维度前插入一个维度。默认值:``0``
返回值:
组合为的张量,形状 :math:`(n_0, \\ldots, n_{\\mathtt{dim} - 1}, \\mathtt{len(x)}, n_{\\mathtt{dim}}, \\ldots, n_k)`
示例代码:
>>> a = jt.array([1, 2, 3])
>>> b = jt.array([4, 5, 6])
>>> jt.stack([a, b])
jt.Var([[1 2 3]
[4 5 6]], dtype=int32)
>>> jt.stack([a, b], dim=1)
jt.Var([[1 4]
[2 5]
[3 6]], dtype=int32)
'''
assert isinstance(x, Sequence)
if len(x) < 2:
return x[0].unsqueeze(dim)
res = [x_.unsqueeze(dim) for x_ in x]
return jt.concat(res, dim=dim)
jt.Var.stack = stack
[文档]
def flip(x, dim=0):
'''
将张量在指定维度镜像翻转
参数:
- x (Var): 需要翻转的张量
- dim (int | tuple[int, ...]): 需要翻转的维度,负数表示从后往前数。默认值:0
返回值:
x 在指定维度翻转后的结果
代码示例:
>>> x = jt.array([[1, 2], [3, 4]])
>>> x.flip()
jt.Var([[3 4]
[1 2]], dtype=int32)
>>> x.flip(1)
jt.Var([[2 1]
[4 3]], dtype=int32)
>>> x.flip((0, 1))
jt.Var([[4 3]
[2 1]], dtype=int32)
'''
if isinstance(dim, int):
dim = [dim]
for i in range(len(dim)):
if dim[i]<0:
dim[i] += x.ndim
assert dim[i]>=0 and dim[i]<x.ndim
dim = set(dim)
tar_dims = []
for i in range(len(x.shape)):
if i in dim:
tar_dims.append(f"xshape{i}-1-i{i}")
else:
tar_dims.append(f"i{i}")
return x.reindex(x.shape, tar_dims)
jt.Var.flip = flip
[文档]
def cross(input, other, dim=-1):
'''
批量计算向量叉乘:
:math:`(a_1, a_2, a_3) \\times (b_1, b_2, b_3) = (a_2 b_3 - a_3 b_2 , a_3 b_1 - a_1 b_3 , a_1 b_2 - a_2 b_1)`
参数:
- input (Var): 叉乘的左参数
- other (Var): 叉乘的右参数,形状与 ``input`` 相同
- dim (int): 计算叉乘的向量所在的维度,要求 ``input`` 和 ``other`` 在 ``dim`` 维的长度都是 3。负数表示从后往前数。默认值:-1
返回值:
``input`` 和 ``other`` 对应向量叉乘的结果,形状与 ``input`` 和 ``other`` 相同
代码示例:
>>> x = jt.randn(2, 3)
>>> y = jt.randn(2, 3)
>>> x, y, x.cross(y)
(jt.Var([[ 0.6452728 -1.7699949 0.7562031 ]
[ 0.32050335 0.7093985 -0.44102713]], dtype=float32),
jt.Var([[ 0.42947495 0.6956272 -0.35488197]
[ 0.44775873 -0.19148853 -0.52726024]], dtype=float32),
jt.Var([[ 0.10210377 0.553766 1.2090378 ]
[-0.45848927 -0.02848507 -0.3790121 ]], dtype=float32))
>>> x.cross(x)
jt.Var([[0. 0. 0.]
[0. 0. 0.]], dtype=float32)
'''
assert input.shape==other.shape, "input shape and other shape must be same"
if dim < 0: dim += len(input.shape)
assert input.shape[dim] == 3, "input dim shape must be 3"
a1 = input[(slice(None,),)*dim+(1,)]*other[(slice(None,),)*dim+(2,)]-input[(slice(None,),)*dim+(2,)]*other[(slice(None,),)*dim+(1,)]
a2 = input[(slice(None,),)*dim+(2,)]*other[(slice(None,),)*dim+(0,)]-input[(slice(None,),)*dim+(0,)]*other[(slice(None,),)*dim+(2,)]
a3 = input[(slice(None,),)*dim+(0,)]*other[(slice(None,),)*dim+(1,)]-input[(slice(None,),)*dim+(1,)]*other[(slice(None,),)*dim+(0,)]
return jt.concat([a1.unsqueeze(dim),a2.unsqueeze(dim),a3.unsqueeze(dim)], dim=dim)
jt.Var.cross = cross
[文档]
def normalize(input, p=2, dim=1, eps=1e-30):
'''
在指定维度对张量 :math:`L_p` 归一化
.. math::
v' = \\frac{v}{\\max(\\lVert v \\rVert_p, \\epsilon)}
参数:
- input (``Var``): 被归一化的张量
- p (``float``): 计算向量范数所用的指数。默认值:2
- dim (``int`` or ``tuple[int, ...]``): 被归一化的维度。默认值:1
- eps (``float``): 避免除以零所用的小量 :math:`\\epsilon` 。默认值: ``1e-30``
返回值:
在 ``dim`` 指定维度归一化后的张量
代码示例:
>>> x = jt.randn(2, 3)
>>> x, x.normalize()
(jt.Var([[ 0.6452728 -1.7699949 0.7562031 ]
[ 0.32050335 0.7093985 -0.44102713]], dtype=float32),
jt.Var([[ 0.31786057 -0.8718973 0.3725047 ]
[ 0.35822764 0.792897 -0.49293745]], dtype=float32))
>>> x.normalize().norm()
jt.Var([1. 1.], dtype=float32)
'''
return input / input.norm(p, dim, True, eps)
jt.Var.normalize = normalize
[文档]
def unbind(x, dim=0):
'''
解除var的一个维度。返回沿着给定维度的所有切片的元组,这个维度会从输出的各个切片中移除。
参数:
- x (``Var``): 需要解除维度的var
- dim (``int``): 需要移除的维度,默认值为 ``0``
返回值:
返回一个 ``List`` ,包含了沿着给定维度的所有切片的元组。
代码示例:
>>> a = jt.random((2,3))
>>> b = jt.unbind(a, 0)
>>> c = jt.unbind(a, 1)
>>> a,b,c
jt.Var([[0.2874082 0.46987164 0.13281713]
[0.2576157 0.7470389 0.48285943]], dtype=float32)
[jt.Var([0.2874082 0.46987164 0.13281713], dtype=float32), jt.Var([0.2576157 0.7470389 0.48285943], dtype=float32)]
[jt.Var([0.2874082 0.2576157], dtype=float32), jt.Var([0.46987164 0.7470389 ], dtype=float32), jt.Var([0.13281713 0.48285943], dtype=float32)]
注意:
如果 ``dim`` 值给定为负数,那么需要首先对其进行处理,使其等于 ``dim`` 加上 ``x`` 的形状的长度之和。
'''
if dim < 0: dim += len(x.shape)
return [x[(slice(None),)*dim+(i,)] for i in range(x.shape[dim])]
jt.Var.unbind = unbind
[文档]
def make_grid(x, nrow=8, padding=2, normalize=False, range=None, scale_each=False, pad_value=0):
'''
将多个图片按网格状拼成一个图片
参数:
- x (Var): 图片数据,形状 :math:`(n, c, h, w)`
- nrow (int): 每行多少个图片,最终拼成的网格形状是 :math:`(n / \\mathtt{nrow}, nrow)` 。默认值:8
- padding (int): 图片间的间隔。默认值:2
- normalize (bool): 是否将图片值线性映射到 :math:`[0, 1]` 。默认值:False
- range (None | tuple[float, float]): 映射到 :math:`[0, 1]` 的范围,或者不指定则使用图片数据中的最小最大值。默认值:None
- scale_each (bool): 不支持,必须为 False。默认值:False
- pad_value (float): 图片间填充的值。默认值:0
示例:
>>> x = jt.randn(100, 3, 64, 64)
>>> grid = jt.make_grid(x)
>>> print(grid.shape)
[3,860,530,]
返回值:
形状 :math:`(c, h', w')` 的图片,由 ``x`` 中的图片组合成网格状而成
'''
assert isinstance(range, tuple) or range is None
assert scale_each == False
if isinstance(x, list): x = jt.stack(x)
assert isinstance(x, jt.Var)
if normalize:
if range is None: x = (x - x.min()) / (x.max() - x.min())
else: x = (x - range[0]) / (range[1] - range[0])
if x.ndim < 4: return x
if x.ndim == 4 and x.shape[0] <= 1: return x
nrow = min(nrow, x.shape[0])
b,c,h,w = x.shape
ncol = math.ceil(b / nrow)
return x.reindex([c, h*ncol+(ncol+1)*padding, w*nrow+(nrow+1)*padding],
[f"i1/{padding+h}*{nrow}+i2/{padding+w}", "i0",
f"i1-i1/{padding+h}*{padding+h}-{padding}", f"i2-i2/{padding+w}*{padding+w}-{padding}"], overflow_value=pad_value)
[文档]
def save_image(
x,
filepath,
nrow: int = 8,
padding: int = 2,
normalize: bool = False,
range = None,
scale_each = False,
pad_value = 0,
format = None
):
'''
将多个图片按网格状拼成一个图片后保存到 ``filepath`` 指定的文件
参数:
- x (``Var``): 图片数据,形状 :math:`(n, c, h, w)`
- filepath (``str``): 保存目标,文件名或打开的文件对象
- nrow (``int``): 每行多少个图片,最终拼成的网格形状是 :math:`(n / \\text{nrow}, \\text{nrow})` 。默认值:``8``
- padding (``int``): 图片间的间隔。默认值:``2``
- normalize (``bool``): 是否将图片值线性映射到 :math:`[0, 1]` 。默认值:``False``
- range (``None, tuple[float, float]``): 映射到 :math:`[0, 1]` 的范围,或者不指定则使用图片数据中的最小最大值。默认值:``None``
- scale_each (``bool``): 不支持,必须为 False。默认值:``False``
- pad_value (``float``): 图片间填充的值。默认值:``0``
- format:保存的文件格式,默认由文件名扩展名确定
示例代码:
>>> x = jt.randn(100, 3, 64, 64)
>>> save_image(x, 'random.png')
'''
from PIL import Image
grid = make_grid(x, nrow=nrow, padding=padding, pad_value=pad_value,
normalize=normalize, range=range, scale_each=scale_each)
ndarr = (grid*255+0.5).clamp(0, 255).permute(1, 2, 0).uint8().numpy()
im = Image.fromarray(ndarr)
im.save(filepath, format=format)
def _ntuple(n):
'''
构造一个将值转换为 ``n`` 元组的函数
参数:
- n (int): 要构造的是几元组
返回值:
一个函数,将输入的可迭代对象不变,不可迭代对象重复 ``n`` 次变成 ``n`` 元组
代码示例:
>>> f = jt.misc._ntuple(3)
>>> f(1)
(1, 1, 1)
>>> f((1, 2, 3))
(1, 2, 3)
'''
def parse(x):
if isinstance(x, Iterable):
return x
return tuple([x]*n)
return parse
_single = _ntuple(1)
_pair = _ntuple(2)
_triple = _ntuple(3)
_quadruple = _ntuple(4)
[文档]
def unique(
input: jt.Var,
return_inverse: bool=False,
return_counts: bool=False,
dim: int=None):
'''
返回张量中去重后的元素
参数:
- input (``Var``): 被去重的张量
- return_inverse (``bool``): 是否返回 ``input`` 每个元素对应去重结果中的不重复元素下标。默认值:``False``
- return_counts (``bool``): 是否返回去重结果中每个不重复元素在 ``input`` 里出现次数,必须同时指定 ``return_inverse=True`` 才有效。默认值:``False``
- dim (``int``): 去重的维度,如果为 ``None`` 则将输入扁平化后再去重。默认值:``None``
返回值:
- 如果 ``return_inverse`` 未指定,则返回 ``Var`` 为去重结果
- 否则,如果 ``return_inverse`` 和 ``return_counts`` 指定,则返回 ``tuple[Var, Var, Var]`` 为去重结果、下标、出现次数
- 否则,仅 ``return_inverse`` 指定,则返回 ``tuple[Var, Var]`` 为去重结果、下标
代码示例:
>>> jt.unique(jt.array([1, 3, 2, 3]))
jt.Var([1 2 3], dtype=int32)
>>> jt.unique(jt.array([1, 3, 2, 3, 2]), return_inverse=True, return_counts=True)
(jt.Var([1 2 3], dtype=int32), jt.Var([0 2 1 2 1], dtype=int32), jt.Var([1 2 2], dtype=int32))
>>> jt.unique(jt.array([[1, 3], [2, 3]]), return_inverse=True)
(jt.Var([1 2 3], dtype=int32), jt.Var([[0 2]
[1 2]], dtype=int32))
>>> jt.unique(jt.array([[1, 3], [1, 3]]), dim=0)
jt.Var([[1 3]], dtype=int32)
'''
temp_shape = None
if dim == None:
temp_shape = list(input.shape)
input_flatten = input.flatten()
dim = 0
else:
input_flatten = input
input_flatten = input_flatten.transpose(dim, 0)
orig_shape = input_flatten.shape
input_flatten = input_flatten.view(orig_shape[0], -1)
with jt.flag_scope(compile_options = {"FLAGS: --extended-lambda ": 1} if jt.flags.use_cuda else {}):
indice = jt.code((input_flatten.shape[0], ), 'int32', [input_flatten],
cpu_header='''
#include <algorithm>
''',
cpu_src='''
@alias(input_flatten, in0)
@alias(indice, out)
int dimlen = input_flatten_shape0, dimsize = input_flatten_shape1;
for(int i = 0; i < dimlen; ++i) @indice(i) = i;
std::sort(&@indice(0), &@indice(dimlen), [&](int a, int b){
for(int i = 0; i < dimsize; ++i) {
int lhs = @input_flatten(a, i), rhs = @input_flatten(b, i);
if (lhs != rhs) return lhs < rhs;
}
return false;
});
''',
cuda_header='''
#undef out
#include <thrust/extrema.h>
#include <thrust/device_ptr.h>
#include <thrust/execution_policy.h>
#include <thrust/device_vector.h>
#include <thrust/sequence.h>
#include <cub/cub.cuh>
#include <executor.h>
''',
cuda_src=
'''
@alias(input_flatten, in0)
@alias(indice, out)
int dimlen = indice_shape0, dimsize = input_flatten_shape1;
if (dimsize == 1) {
size_t raw_allocation, d_allocation, temp_storage_bytes = 0;
void *d_temp_storage = NULL;
int32_t* raw_ptr = (int32_t*)exe.allocator->alloc(dimlen * (sizeof(int32_t) + sizeof(input_flatten_type)), raw_allocation);
thrust::device_ptr<int32_t> arange_ptr = thrust::device_pointer_cast(raw_ptr);
thrust::sequence(arange_ptr, arange_ptr + dimlen);
cub::DeviceRadixSort::SortPairs(d_temp_storage, temp_storage_bytes, input_flatten_p,
(input_flatten_type*)(raw_ptr + dimlen), thrust::raw_pointer_cast(arange_ptr), indice_p, dimlen);
d_temp_storage = exe.allocator->alloc(temp_storage_bytes, d_allocation);
cub::DeviceRadixSort::SortPairs(d_temp_storage, temp_storage_bytes, input_flatten_p,
(input_flatten_type*)(raw_ptr + dimlen), thrust::raw_pointer_cast(arange_ptr), indice_p, dimlen);
exe.allocator->free(raw_ptr, dimlen * (sizeof(int) + sizeof(input_flatten_type)), raw_allocation);
exe.allocator->free(d_temp_storage, temp_storage_bytes, d_allocation);
} else {
thrust::device_ptr<input_flatten_type> input_ptr = thrust::device_pointer_cast(input_flatten_p);
thrust::device_ptr<int32_t> indice_ptr = thrust::device_pointer_cast(indice_p);
thrust::sequence(indice_ptr, indice_ptr + dimlen);
thrust::sort(thrust::device, indice_ptr, indice_ptr + dimlen,
[=] __device__ (int32_t a, int32_t b)->bool {
for(int i = 0; i < dimsize; ++i) {
input_flatten_type lhs = input_ptr[i + a * dimsize],
rhs = input_ptr[i + b * dimsize];
if (lhs != rhs) return lhs < rhs;
}
return false;
});
}
'''
)
input_sorted = input_flatten[indice][:]
dimlen = indice.shape[0]
diff = jt.logical_not(jt.all(input_sorted[1:] == input_sorted[: -1], 1))
diff = jt.concat([jt.Var([False]), diff], 0)
diff = jt.array(diff, dtype = jt.int32)
with jt.flag_scope(compile_options = {"FLAGS: --extended-lambda ": 1} if jt.flags.use_cuda else {}):
output, inverse = jt.code(
[(-input_sorted.shape[0], ), (indice.shape)],
[input_sorted.dtype, indice.dtype],
[input_sorted, diff, indice],
cpu_header='''
#include <algorithm>
@alias(input_sorted, in0)
@alias(diff, in1)
@alias(indice, in2)
@alias(output, out0)
@alias(inverse, out1)
''',
cpu_src=
f"bool return_inverse = {int(return_inverse)};" +
'''
int tot = -1;
for (int i = 0; i < input_sorted_shape0; ++i) {
if (i == 0 || @diff(i)) {
++tot; @output(tot) = i;
}
if (return_inverse)
@inverse(@indice(i)) = tot;
}
output->set_shape({tot + 1});
''',
cuda_header='''
#undef out
#include <thrust/extrema.h>
#include <thrust/device_ptr.h>
#include <thrust/execution_policy.h>
#include <thrust/scan.h>
#include <executor.h>
@alias(input_sorted, in0)
@alias(diff, in1)
@alias(indice, in2)
@alias(output, out0)
@alias(inverse, out1)
''',
cuda_src=
f"bool return_inverse = {int(return_inverse)};" +
'''
int dimlen = input_sorted_shape0, dimsize = input_sorted_shape1;
size_t raw_allocation;
int32_t* raw_ptr = (int32_t*)exe.allocator->alloc(2 * dimlen * sizeof(int), raw_allocation);
thrust::device_ptr<int32_t> diff_ptr = thrust::device_pointer_cast(diff_p),
inverse_ptr = thrust::device_pointer_cast(inverse_p),
array_ptr = thrust::device_pointer_cast(raw_ptr),
sum_ptr = thrust::device_pointer_cast(raw_ptr + dimlen),
indice_ptr = thrust::device_pointer_cast(indice_p);
thrust::device_ptr<input_sorted_type> input_ptr = thrust::device_pointer_cast(input_sorted_p);
if (return_inverse) {
thrust::inclusive_scan(diff_ptr, diff_ptr + dimlen, sum_ptr);
thrust::scatter(sum_ptr, sum_ptr + dimlen, indice_ptr, inverse_ptr);
}
thrust::sequence(array_ptr, array_ptr + dimlen);
int32_t num = thrust::unique(array_ptr, array_ptr + dimlen,
[=] __device__ (int32_t a, int32_t b)->bool {
for(int i = 0; i < dimsize; ++i) {
input_sorted_type lhs = input_ptr[i + a * dimsize],
rhs = input_ptr[i + b * dimsize];
if (lhs != rhs) return false;
}
return true;
}) - array_ptr;
cudaMemcpy(output_p, raw_ptr, sizeof(int32_t) * num, cudaMemcpyDeviceToDevice);
exe.allocator->free(raw_ptr, 2 * dimlen * sizeof(int32_t), raw_allocation);
output->set_shape({ num });
'''
)
indice_shape = (output.shape[0], )
output = input_sorted[output][:]
new_shape = list(orig_shape[1:])
new_shape.insert(0, -1)
output = output.view(new_shape).transpose(dim, 0)
if temp_shape != None:
inverse = inverse.view(temp_shape).transpose(dim, 0)
if return_inverse:
if return_counts:
counts = jt.zeros(indice_shape, dtype=jt.int32)
jt.scatter_(counts, 0, inverse.flatten(), jt.ones(dimlen), reduce='add')
return output, inverse, counts
else:
return output, inverse
else:
return output
jt.Var.unique = unique
[文档]
def hypot(a,b):
'''
计算输入平方之和的平方根 :math:`\\sqrt{a^2 + b^2}`
参数:
- a (Var): 输入参数
- b (Var): 输入参数,形状和 ``b`` 一样
返回值:
``a`` 和 ``b`` 平方之和的平方根
代码示例:
>>> a = jt.array([3.0, 5.0])
>>> b = jt.array([4.0, 12.0])
>>> jt.hypot(a, b)
jt.Var([ 5. 13.], dtype=float32)
'''
return jt.sqrt(a.sqr()+b.sqr())
[文档]
def rad2deg(x):
'''
将弧度转换为角度
.. math::
x' = \\frac{180 x}{\\pi}
参数:
- x (``Var``): 弧度值组成的张量
返回值:
``x`` 中每个元素换成角度
代码示例:
>>> x = jt.array([[3.142, -3.142], [6.283, -6.283], [1.570, -1.570]])
>>> x.rad2deg()
jt.Var([[ 180.02333 -180.02333]
[ 359.98935 -359.98935]
[ 89.95437 -89.95437]], dtype=float32)
'''
return 180 * x / np.pi
jt.Var.rad2deg = rad2deg
[文档]
def deg2rad(x):
'''
将输入中的每一个元素从角度值(以度为单位)转换为弧度值。转换的公式为:
.. math::
rad = deg * \\frac{\\pi}{180}
参数:
- x (Var) : 输入的角度值,可以是单一浮点数或任意形状的输入 Var
返回:
float 或 Var: 转换后的弧度值,其形状与输入相同。
代码示例:
>>> input = jt.float32([[180.0, -180.0], [360.0, -360.0], [90.0, -90.0]])
>>> jt.deg2rad(a)
jt.Var([[ 3.141593 -3.141593 ]
[ 6.283186 -6.283186 ]
[ 1.5707965 -1.5707965]], dtype=float32)
'''
return x * np.pi / 180.
jt.Var.deg2rad = deg2rad
[文档]
def arctan2(y,x):
'''
按元素计算y/x的反正切值,以弧度计算。注意第一个参数 y 中的元素是 y 坐标,第二个参数 x 中的元素是 x 坐标。
.. math::
\\text{arctan2}(y, x) =
\\begin{cases}
\\arctan(y / x) - \\pi, & \\text{ if } x \\lt 0, y \\lt 0 \\\\
\\arctan(y / x) + \\pi, & \\text{ if } x \\lt 0, y \\geq 0 \\\\
\\arctan(y / x), & \\text{ otherwise }
\\end{cases}
参数:
- y (Var): Jittor数组,任何shape和dtype。
- x (Var): Jittor数组,与y相同的shape和dtype。+
返回:
Jittor数组,具有和输入相同的shape和dtype。
代码示例:
>>> x = jt.randn(3)
jt.Var([0.8616086 0.81152385 0.0437891], dtype=float32)
>>> y = jt.randn(3)
jt.Var([-0.0904924 1.4761028 -1.367789], dtype=float32)
>>> c = jt.arctan2(y, x)
jt.Var([-0.10464364 1.0681262 -1.5387927], dtype=float32)
'''
angle = jt.zeros(x.shape,dtype=x.dtype)
x = (x!=0.0).ternary(x, 1e-30)
angle = (y/x).arctan()
mask = (x<0)&(y<0)
angle = angle - mask*np.pi
mask = (x<0)&(y>=0)
angle = angle + mask*np.pi
return angle
atan2 = arctan2
[文档]
def nonzero(x):
'''
返回输入Var中每一个非零元素的索引。
参数:
x (``Var``): 任意形状的输入 ``Var``
返回值:
返回一个二维 Var, 每一行代表一个非零元素的索引。注意每一行的长度取决于输入 Var 的维度。
代码示例:
>>> x = jt.array([0, 1, 2, 0, 3, 0])
>>> y = jt.array([[0, 1, 2], [0, 3, 4], [0, 0, 1]])
>>> jt.nonzero(x)
jt.Var([[1]
[2]
[4]], dtype=int32)
>>> jt.nonzero(y)
jt.Var([[0 1]
[0 2]
[1 1]
[1 2]
[2 2]], dtype=int32)
'''
x = jt.where(x)
x = [xx.unsqueeze(1) for xx in x]
if len(x)<2:
return x[0]
x = jt.concat(x,dim=1)
return x
jt.Var.nonzero = nonzero
[文档]
def arange(start=0, end=None, step=1,dtype=None):
'''
生成一个包含在区间[ ``start`` , ``end`` )内等间隔分布的数字的序列。
参数:
- start(float | Var, 可选): 默认值=0, 序列的开始值。也可以是一个仅包含一个元素的 Var
- end(float | Var, 可选): , 默认值=None, 序列的结束值,也可以是一个仅包含一个元素的 Var
- step(float | Var, 可选):, 默认值=1, 生成的数字序列之间的固定间隔,也可以是一个仅包含一个元素的 Var
- dtype(type, 可选): 把返回的 Var 转换为指定的数据类型。
代码示例:
>>> jt.arange(1, 3.5, 0.5, jt.float32)
jt.Var([1. 1.5 2. 2.5 3. ], dtype=float32)
返回值:
一个 1-D 的 Var 对象,包含从 ``start`` (包含)到 ``end`` (不包含)以 ``step`` 为步长的等差序列。
'''
if isinstance(start, jt.Var): start = start.item()
if isinstance(end, jt.Var): end = end.item()
if isinstance(step, jt.Var): step = step.item()
if end is None:
end,start = start,0
l = round((end-start)//step)+1
if (l-1)*step+start>=end:
l-=1
x = jt.index((l,),0)
x = x*step+start
if dtype is not None:
x= x.cast(dtype)
return x
[文档]
def log2(x):
'''
计算 ``x`` 中每个元素的以 2 为底的对数值。
.. math::
y_i=\\log _2\\left(x_i\\right)
参数:
x(Var): 任意形状的输入 Var
代码示例:
>>> jt.array([1,2,4,8,10]).log2()
jt.Var([0. 1. 2. 3. 3.321928], dtype=float32)
返回值:
返回与输入形状相同的 Var, 其元素为输入元素的以2为底的对数。
'''
return jt.log(x)/math.log(2.0)
jt.Var.log2 = log2
[文档]
def meshgrid(*tensors):
'''
创建由 ``*tensors`` 这个输入的 Var 列表指定的坐标网格列表。
给定 :math:`N` 个 1D 张量 :math:`T_0 \\ldots T_{N-1}` 作为输入,输入对应的尺寸分别为 :math:`S_0 \\ldots S_{N-1}`,输出 :math:`N` 个 :math:`N` 维张量 :math:`G_0 \\ldots G_{N-1}`,每个形状为 :math:`\\left(S_0, \\ldots, S_{N-1}\\right)`,其中输出 :math:`G_i` 是通过将 :math:`T_i` 扩展到结果形状而构建的。
参数:
``*tensors``: (jt.Var, 不限个数)- 输入的张量, 每个张量需要是一维的jt.Var向量。
代码示例:
>>> a = jt.array([1, 2, 3])
>>> b = jt.array([4, 5, 6, 7])
>>> jt.meshgrid(a, b)
[jt.Var([[1 1 1 1]
[2 2 2 2]
[3 3 3 3]], dtype=int32),
jt.Var([[4 5 6 7]
[4 5 6 7]
[4 5 6 7]], dtype=int32)]
返回值:
grids(Var): 包含了 N 个 Var 的列表, 由输入张量列表决定。
'''
if len(tensors)==1 and isinstance(tensors[0], list):
tensors = tensors[0]
size = len(tensors)
shape = []
for i in range(size):
assert isinstance(tensors[i],jt.Var) and tensors[i].ndim==1
shape.append(tensors[i].shape[0])
grids = []
view_shape = [1]*size
for i in range(size):
vs = view_shape[:]
vs[i]=-1
grids.append(tensors[i].reshape(vs).expand(shape))
return grids
[文档]
def split(d, split_size, dim=0):
'''
将张量分割成多个块。每个块都是原始张量的一个视图。
如果 ``split_size`` 是整型,那么张量将被平均分割成等大小的块(如果可能的话)。如果给定维度 ``dim`` 上的张量大小不能被 ``split_size`` 整除,那么最后一个块的大小将会小于其他块。
如果 ``split_size`` 是列表,那么张量将被分割成 ``len(split_size)`` 块,每个块在 ``dim`` 维度上的大小将按照 ``split_size`` 列表指定。
参数:
- d (``Var``): 需要被分割的张量 Var, 任意形状。
- split_size (``int``, ``list(int)``): 单个块的大小,或者每个块的大小的列表。注意如果这里传入列表的话,应当确保列表的元素之和等于输入张量在给定分割维度上的长度 ``d.shape[dim]``。
- dim (``int``): 需要分割的张量的维度。默认值为 ``0``。
代码示例:
>>> a = jt.arange(10).reshape(5, 2))
>>> jt.split(a, 2)
(jt.Var([[0 1]
[2 3]], dtype=int32),
jt.Var([[4 5]
[6 7]], dtype=int32),
jt.Var([[8 9]], dtype=int32))
>>> jt.split(a, [3, 1, 1])
(jt.Var([[0 1]
[2 3]
[4 5]], dtype=int32),
jt.Var([[6 7]], dtype=int32),
jt.Var([[8 9]], dtype=int32))
返回值:
分割后的张量块,以元组的形式返回: ``Tuple[Var, ...]``
'''
if isinstance(split_size,int):
shape = d.shape[dim]
if shape % split_size == 0:
split_size = [split_size]*(shape//split_size)
else:
split_size = [split_size]*(shape//split_size)+[shape%split_size]
if isinstance(split_size, Iterable):
assert sum(split_size)==d.shape[dim]
if dim<0:
dim+=d.ndim
ans = []
last = 0
s_last = len(split_size)-1
gopt_disable = jt.flags.gopt_disable
for j, i in enumerate(split_size):
if i==0:
shape = list(d.shape)
shape[dim]=0
new_d = jt.zeros(tuple(shape),dtype=d.dtype)
ans.append(new_d)
continue
ss = (slice(None),)*dim+(slice(last,last+i),)
if gopt_disable:
new_d = d.getitem(ss)
else:
new_d, d = d.getitem(ss, int(j==s_last))
last +=i
ans.append(new_d)
return tuple(ans)
jt.Var.split = split
[文档]
def tolist(x):
'''
将输入的 Jittor Var 类型变量转化为 Python 的 list 类型。
参数:
x (``Var``): 任意形状的输入张量 ``Var``。
返回值:
转化后的 Python 列表类型 (``list``)
代码示例:
>>> a = jt.array([1,2,3])
>>> jt.tolist(a)
[1, 2, 3]
'''
return x.numpy().tolist()
jt.Var.tolist = tolist
[文档]
def view_as(x,y):
'''
以 ``y`` 的形状对 ``x`` 进行 ``reshape``。
也就是说,对 ``x`` 中的元素进行重排,使得 ``x`` 与 ``y`` 的形状相同。注意,``x`` 中的元素数量必须与 ``y`` 中的元素数量相同,否则报错。
参数:
- x (``Var``): 需要重塑的输入张量 Var。
- y (``Var``): 作为参照形状的的输入张量 Var。
代码示例:
>>> x = jt.arange(4)
>>> y = jt.array([[1,2],[3,4]])
>>> jt.view_as(x, y)
jt.Var([[0 1]
[2 3]], dtype=int32)
返回值:
重塑后的 ``Var``, 数据来自 ``x``, 形状和 ``y`` 相同。
'''
return x.reshape(y.shape)
jt.Var.view_as = view_as
[文档]
def diag(x,diagonal=0):
'''
从向量构建对角矩阵或从方阵中提取对角元素。
如果输入是一个一维向量,返回一个二维方阵,其对角线上的元素由输入向量中的元素构成。
如果输入是一个二维方阵,返回一个由它的对角元素构成的一维向量。
对角线的偏移量可以用 ``diagonal`` 确定,``diagonal = 0`` 代表主对角线, ``diagonal > 0`` 为沿右上方移动, ``diagonal < 0`` 为沿左下方移动。
参数:
- x (Var): 输入张量 Var, 向量或矩阵。
- diagonal (int): 对角偏移量,默认为 0。
代码示例:
>>> a = jt.arange(4)
>>> jt.diag(a)
jt.Var([[0 0 0 0]
[0 1 0 0]
[0 0 2 0]
[0 0 0 3]], dtype=int32)
>>> b = jt.randn(2,2)
jt.Var([[-0.36892104 1.5115978 ]
[ 0.8735136 0.81891364]], dtype=float32)
>>> jt.diag(b)
jt.Var([-0.36892104, 0.81891364], dtype=float32)
返回值:
向量或方阵 (Var)
'''
assert x.ndim==1 or (x.ndim==2 and x.shape[0]==x.shape[1])
d = diagonal if diagonal>=0 else -diagonal
d_str = f'+{diagonal}' if diagonal>=0 else f'{diagonal}'
if x.ndim==1:
output_shape = (x.shape[0]+d,)*2
return x.reindex(output_shape,[f'i1-{d}' if diagonal>=0 else f'i0-{d}'],overflow_conditions=[f'i0{d_str}!=i1'])
else:
output_shape = (x.shape[0]-d,)
return x.reindex(output_shape,[f'i0+{d}' if diagonal<=0 else 'i0',f'i0+{d}' if diagonal>=0 else 'i0'])
jt.Var.diag = diag
[文档]
def topk(input, k, dim=None, largest=True, sorted=True):
'''
在指定维度上保留最大/最小的 ``k`` 个元素
该函数实现了在输入 Var ``input`` 的指定维度上找出最大(或最小)的 ``k`` 个元素,并返回这些元素的值以及其在原始 Var 中的索引。返回的 Var 的大小与 ``input`` 一致,除了在指定的维度上大小为 ``k``
参数:
- input (``Var``): 输入张量 Var, 任意形状
- k (``int``): 指定寻找最大/最小元素个数
- dim (``int``): 指定在哪个维度上寻找最大/最小元素,默认为最后一个维度
- largest (``bool``): 为 ``True`` 时,返回最大的 ``k`` 个元素;为 ``False`` 时,返回最小的 ``k`` 个元素。默认为 ``True``
- sorted (``bool``): 为 ``True`` 时,返回的 ``k`` 个元素按照从大到小的顺序排列;为 ``False`` 时,返回的 ``k`` 个元素顺序是未指定的。默认为 ``True``
返回值:
``Tuple[Var]``, 其中包含 ``input`` 在指定维度上仅保留最大/最小 ``k`` 个元素后的张量 (Var),以及这些元素在 ``input`` 中的索引 (``Var``)。
代码示例:
>>> var = jt.array([[1, 2, 3, 4],[5, 6, 7, 8],[9, 10, 11, 12]])
>>> jt.topk(var, 2, dim=0)
(jt.Var([[ 9 10 11 12]
[ 5 6 7 8]], dtype=int32),
jt.Var([[2 2 2 2]
[1 1 1 1]], dtype=int32))
>>> jt.topk(var, 2, dim=1)
(jt.Var([[ 4 3]
[ 8 7]
[12 11]], dtype=int32),
jt.Var([[3 2]
[3 2]
[3 2]], dtype=int32))
'''
if input.numel()==0:
return jt.array([],dtype=input.dtype),jt.array([],dtype='int32')
if dim is None:
dim = -1
if dim<0:
dim+=input.ndim
index,values = jt.argsort(input,dim=dim,descending=largest)
dims = (slice(None),)*dim+(slice(0,k),)
indices = index[dims]
values = values[dims]
return values,indices
jt.Var.topk = topk
[文档]
def kthvalue(input, k, dim=None, keepdim=False, keepdims=False):
'''
在指定维度上保留最小的第 ``k`` 个元素
在输入 Var ``input`` 的指定维度上找出最小的第 ``k`` 个元素,并返回这些元素的值以及其在原始 Var 中的索引。返回的 Var 的大小在指定的维度上大小为 1,其他维度与 ``input`` 一致。
参数:
- input (Var): 输入张量 Var, 任意形状
- k (int): 指定寻找第 k 小的元素
- dim (int): 指定在哪个维度上寻找第 k 小的元素,默认为最后一个维度
- keepdim (bool): 为 ``True`` 时,则在输出中保持缩减的维度。默认为 ``False``
- keepdims (bool): 和 keepdim 的作用相同。旨在与numpy对齐,其在numpy中采用的参数名称是keepdims。默认为 ``False``
代码示例:
>>> var = jt.arange(1, 7).reshape(2, 3)
jt.Var([[1 2 3]
[4 5 6]], dtype=int32)
>>> jt.misc.kthvalue(var, 2, dim=0, keepdim=True)
(jt.Var([[4 5 6]], dtype=int32), jt.Var([[1 1 1]], dtype=int32))
>>> jt.misc.kthvalue(var, 2, dim=0, keepdim=False)
(jt.Var([4 5 6], dtype=int32), jt.Var([1 1 1], dtype=int32))
返回值:
Tuple of Var, 其中包含 ``input`` 在指定维度上仅保留第 ``k`` 小元素后的张量 (Var),以及这些元素在 ``input`` 中的索引 (Var)。
'''
keepdim = keepdim or keepdims
if dim is None:
dim = -1
if dim<0:
dim+=input.ndim
index,values = jt.argsort(input,dim=dim)
dims = (slice(None),)*dim+(slice(k-1,k),)
indices = index[dims]
values = values[dims]
if not keepdim and indices.ndim>1:
indices = indices.squeeze(dim)
values = values.squeeze(dim)
return values,indices
jt.Var.kthvalue = kthvalue
def _prod(x,dim=0):
'''
沿着指定维度计算输入张量在这个维度上所有元素的乘积。
参数:
- x (Var): 输入张量 Var, 任意形状
- dim (int): 指定要缩减的维度。默认为 0。但未填写dim时会默认计算所有元素乘积。
代码示例:
>>> a = jt.randn(4, 2)
jt.Var([[-0.09882022 1.0750706 ]
[ 0.7829371 1.4028096 ]
[-0.62049896 1.2268904 ]
[ 0.3696484 -1.2867309 ]], dtype=float32)
>>> jt.prod(a, 1)
jt.Var([-0.10623872 1.0983117 -0.76128423 -0.475638 ], dtype=float32)
返回值:
Var,输出的张量
'''
x = jt.log(x)
x = x.sum(dim=dim)
return jt.exp(x)
[文档]
def numpy_cumsum(x, dim=None):
''' 沿着指定维度计算输入张量在这个维度上的累加求和。
累加求和是指,在指定方向上:
.. math::
y_i=x_1+x_2+x_3+\cdots+x_i
注意,该函数不应该直接调用。推荐使用jittor.misc.cumsum。
参数:
- x (Var): 输入张量 Var, 任意形状
- dim (int): 指定进行累加求和的轴。默认为最后一维。
代码示例:
>>> a = jt.randn(4, 2)
jt.Var([[ 0.7793252 -2.1805465 ]
[-0.46900165 2.0724497 ]
[-1.2677554 0.31553593]
[ 0.14260457 -1.4511695 ]], dtype=float32)
>>> jt.numpy_cumsum(a, 1)
jt.Var([[ 0.7793252 -1.4012213 ]
[-0.46900165 1.603448 ]
[-1.2677554 -0.9522195 ]
[ 0.14260457 -1.3085649 ]], dtype=float32)
>>> jt.numpy_cumsum(a, 0)
jt.Var([[ 0.7793252 -2.1805465 ]
[ 0.31032354 -0.10809684]
[-0.95743185 0.2074391 ]
[-0.81482726 -1.2437304 ]], dtype=float32)
返回值:
Var,输出的张量,累加求和后的结果
'''
def cumsum_forward(np, data):
a = data['inputs'][0]
b = data['outputs'][0]
np.cumsum(a, axis=dim, out=b)
def cumsum_backward(np, data):
dout = data['dout']
out = data['outputs'][0]
np.cumsum(np.flip(dout, dim), axis=dim, out=out)
np.copyto(out, np.flip(out, dim))
if (dim == None):
dim = -1
assert(dim >= -1 and dim < len(x.shape))
return jt.numpy_code(x.shape, x.dtype, [x], cumsum_forward, [cumsum_backward])
[文档]
def cub_cumsum(x, dim=None):
''' 沿着指定维度计算输入张量在这个维度上的累加求和,使用CUB实现。
累加求和是指,在指定方向上:
.. math::
y_i=x_1+x_2+x_3+\cdots+x_i
注意,该函数不应该直接调用。推荐使用jittor.misc.cumsum。该函数不支持 CPU 模式,需要先指定在 GPU 上计算。
参数:
- x (Var): 输入张量 Var, 任意形状
- dim (int): 指定进行累加求和的轴。默认为最后一维。
代码示例:
>>> jt.flags.use_cuda = 1 # 使用 GPU 计算
>>> a = jt.randn(4, 2)
jt.Var([[ 0.7793252 -2.1805465 ]
[-0.46900165 2.0724497 ]
[-1.2677554 0.31553593]
[ 0.14260457 -1.4511695 ]], dtype=float32)
>>> jt.cub_cumsum(a, 1)
jt.Var([[ 0.7793252 -1.4012213 ]
[-0.46900165 1.603448 ]
[-1.2677554 -0.9522195 ]
[ 0.14260457 -1.3085649 ]], dtype=float32)
>>> jt.cub_cumsum(a, 0)
jt.Var([[ 0.7793252 -2.1805465 ]
[ 0.31032354 -0.10809684]
[-0.95743185 0.2074391 ]
[-0.81482726 -1.2437304 ]], dtype=float32)
返回值:
Var,输出的张量,累加求和后的结果
'''
if (dim == None):
dim = -1
assert(dim >= -1 and dim < len(x.shape))
shape = list(x.shape)
if (dim != -1 and dim != len(shape) - 1):
order = list(range(len(shape)))
order[dim], order[-1] = order[-1], order[dim]
shape[dim], shape[-1] = shape[-1], shape[dim]
x = x.permute(order)
if (len(shape) > 2):
x = x.reshape([-1, shape[-1]])
x = jt.compile_extern.cub_ops.cub_cumsum(x)
if (len(shape) > 2):
x = x.reshape(shape)
if (dim != -1 and dim != len(shape) - 1):
x = x.permute(order)
return x
[文档]
def cumsum(x, dim=None):
''' 沿着指定维度计算输入张量在这个维度上的累加求和。
累加求和是指,在指定方向上:
.. math::
y_i=x_1+x_2+x_3+\cdots+x_i
推荐直接调用该函数计算 cumsum。取决于是否指定在 GPU 上计算,该函数会调用不同的算子实现。
参数:
- x (Var): 输入张量 Var, 任意形状
- dim (int): 指定进行累加求和的轴。默认为最后一维。
代码示例:
>>> a = jt.randn(4, 2)
jt.Var([[ 0.7793252 -2.1805465 ]
[-0.46900165 2.0724497 ]
[-1.2677554 0.31553593]
[ 0.14260457 -1.4511695 ]], dtype=float32)
>>> jt.cumsum(a, 1)
jt.Var([[ 0.7793252 -1.4012213 ]
[-0.46900165 1.603448 ]
[-1.2677554 -0.9522195 ]
[ 0.14260457 -1.3085649 ]], dtype=float32)
>>> jt.cumsum(a, 0)
jt.Var([[ 0.7793252 -2.1805465 ]
[ 0.31032354 -0.10809684]
[-0.95743185 0.2074391 ]
[-0.81482726 -1.2437304 ]], dtype=float32)
返回值:
Var,输出的张量,累加求和后的结果
'''
if (dim == None):
dim = -1
assert(dim >= -1 and dim < len(x.shape))
if jt.flags.use_cuda:
return cub_cumsum(x, dim)
else:
return numpy_cumsum(x, dim)
jt.Var.cumsum = cumsum
[文档]
def cumprod(x,dim=None):
'''
沿着指定维度计算输入张量在这个维度上的累乘。
累乘是指,在指定方向上:
.. math::
y_i=x_1 \\times x_2 \\times x_3 \\times \\cdots \\times x_i
注意,由于此函数的实现是先取 :math:`\\log` 再做累加,因此务必保证输入为正数,否则计算结果会有 ``nan``。
参数:
- x (Var): 输入张量 Var, 任意形状
- dim (int): 指定进行乘法的轴。默认为最后一维。
代码示例:
>>> a = jt.arange(1, 7).reshape(2, 3)
jt.Var([[1 2 3]
[4 5 6], dtype=int32)
>>> jt.cumprod(a, 1)
jt.Var([[ 1. 2. 6. ]
[ 4. 20. 120.00001]], dtype=float32)
>>> jt.cumprod(a, 0)
jt.Var([[ 1. 2. 3.]
[ 4. 10. 18.]], dtype=float32)
返回值:
Var,输出的张量,累乘后的结果,形状和输入一致
'''
x = jt.log(x)
x = cumsum(x,dim=dim)
return jt.exp(x)
jt.Var.cumprod=cumprod
[文档]
def nms(dets,thresh):
'''
对给定的一个未排序的矩阵,执行非极大值抑制(non-maximum suppression, NMS)操作。
输入应当是一个二维矩阵,矩阵的每一行代表一个边界框,格式为: ``[x1, y1, x2, y2, score]`` ,其中, ``[x1, y1]`` 为左上角的坐标, ``[x2, y2]`` 为右下角的坐标, ``score`` 是该边界框对应的置信度分数。
使用的是贪婪策略,首先将所有边界框按照置信度得分降序排序,然后从中选取分数最高的边界框并移除所有与该边界框的 IoU 值大于阈值的边界框。然后对剩余的边界框重复上述过程直到没有边界框,最后返回被选出的边界框的索引。
IoU 是衡量两个边界框重叠程度的指标,其定义为两个边界框的交集面积除以并集面积。
参数:
- dets (Var): 输入的 ``Var``, shape 为 ``(N, 5)``, N代表边界框的个数,5列分别代表 ``[x1, y1, x2, y2, score]``
- thresh (float): IoU 阈值,IoU 值大于此的边界框将被移除
返回值:
被选出的边界框的索引,类型为 ``Var``,在原 ``dets`` 中的行索引。
代码示例:
>>> a = dets = jt.array([[20, 20, 40, 40, 0.9],[22, 22, 42, 42, 0.8],[200, 200, 240, 240, 0.7]], dtype="float32")
>>> jt.nms(dets, 0.5)
jt.Var([0 2], dtype=int32)
'''
threshold = str(thresh)
order = jt.argsort(dets[:,4],descending=True)[0]
dets = dets[order]
s_1 = '(@x(j,2)-@x(j,0)+1)*(@x(j,3)-@x(j,1)+1)'
s_2 = '(@x(i,2)-@x(i,0)+1)*(@x(i,3)-@x(i,1)+1)'
s_inter_w = 'max((Tx)0,min(@x(j,2),@x(i,2))-max(@x(j,0),@x(i,0))+1)'
s_inter_h = 'max((Tx)0,min(@x(j,3),@x(i,3))-max(@x(j,1),@x(i,1))+1)'
s_inter = s_inter_h+'*'+s_inter_w
iou = s_inter + '/(' + s_1 +'+' + s_2 + '-' + s_inter + ')'
fail_cond = iou+'>'+threshold
selected = jt.candidate(dets, fail_cond)
return order[selected]
jt.Var.expand_as = jt.Var.broadcast_var
[文档]
def index_fill_(x,dim,indexs,val):
'''
根据给定的索引 ``indexs`` 在指定维度 ``dim`` 上替换为给定元素值 ``val``
参数:
- x (Var): 输入的 Var, 任意形状
- dim (int): 进行索引替换的维度
- indexs (Var): 需要替换的索引,类型为 Var,shape 为 (N, ),N 代表需要替换的索引个数
- val (float): 替换的元素值
代码示例:
>>> x = jt.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=\"float32\")
>>> jt.index_fill_(x, 0, jt.array([0, 2]), -1)
jt.Var([[-1. -1. -1.]
[ 4. 5. 6.]
[-1. -1. -1.]], dtype=float32)
>>> jt.index_fill_(x, 1, jt.array([0, 2]), -1)
jt.Var([[-1. 2. -1.]
[-1. 5. -1.]
[-1. 8. -1.]], dtype=float32)
返回值:
进行索引替换之后的 Var,shape 与输入的 Var 一致
'''
overflow_conditions = [f'i{dim}=={i}'for i in indexs]
indexs = [f'i{i}' for i in range(len(x.shape))]
return x.reindex(shape = x.shape,indexes = indexs,overflow_conditions=overflow_conditions,overflow_value=val)
[文档]
def triu_(x,diagonal=0):
'''
返回输入二维矩阵或批量矩阵输入 ``x`` 的上三角部分,其余部分为 0。注意输入矩阵可以不是方阵。
对角线的偏移量可以用 ``diagonal`` 确定,``diagonal`` = 0 代表主对角线, ``diagonal`` > 0 为沿右上方移动, ``diagonal`` < 0 为沿左下方移动。
参数:
- x (``Var``): 输入的 ``Var``, 维度需要大于等于 ``2``
- diagonal (``int``): 对角偏移量,默认为 ``0``。
代码示例:
>>> x = jt.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=\"float32\")
>>> jt.triu_(x, 0)
jt.Var([[1. 2. 3.]
[0. 5. 6.]
[0. 0. 9.]], dtype=float32)
>>> jt.triu_(x, 1)
jt.Var([[0. 2. 3.]
[0. 0. 6.]
[0. 0. 0.]], dtype=float32)
>>> jt.triu_(x, -1)
jt.Var([[1. 2. 3.]
[4. 5. 6.]
[0. 8. 9.]], dtype=float32)
返回值:
基于输入的上三角矩阵, ``Var``
'''
l = len(x.shape)
assert l>1
overflow_conditions=[f'i{l-1}<i{l-2}+{diagonal}']
indexs = [f'i{i}' for i in range(l)]
return x.reindex(x.shape,indexs,overflow_conditions=overflow_conditions,overflow_value=0)
jt.Var.triu_ = triu_
def print_tree(now, max_memory_size, prefix1, prefix2, build_by):
def format_size(s, end='B'):
if (s < 1024):
s = str(s)
return s + ' '+end
if (s < 1024*1024):
s = format(s/1024, '.2f')
return s + ' K'+end
if (s < 1024*1024*1024):
s = format(s/1024/1024, '.2f')
return s + ' M'+end
s = format(s/1024/1024/1024, '.2f')
return s + ' G'+end
out = ''
tab = ' '
out += prefix1+now['name']+'('+now['type']+')'
out += prefix2+'['+format_size(now['size'])+'; '+format(now['size']/max_memory_size*100, '.2f')+'%; cnt:'+format_size(now['cnt'],'') + ']'
if len(now['children']) == 0 and len(now['vinfo']):
out += prefix2+now['vinfo'][0]
if len(now['vinfo']) > 1: out += "..."
out += ' '
if (build_by == 0):
for p in now['path']:
out += prefix2+p+' '
else:
out += prefix2+now['path'] + ' '
if (len(now['children']) > 0):
out += prefix2 + tab + '| ' + ' '
else:
out += prefix2 + ' '
for i in range(len(now['children'])):
c = now['children'][i]
if i < len(now['children']) - 1:
prefix1_ = prefix2 + tab + '├─'
prefix2_ = prefix2 + tab + '| '
else:
prefix1_ = prefix2 + tab + '└─'
prefix2_ = prefix2 + tab + ' '
out += print_tree(c, max_memory_size, prefix1_, prefix2_, build_by)
return out
[文档]
def get_max_memory_treemap(build_by=0, do_print=True):
'''
显示最大内存消耗的树图。
参数:
- build_by (int, 可选): 0或1,0表示按照名称构造树,1表示按照路径构造树。默认值: 0
- do_print (bool, 可选): 是否打印输出。如果do_print=True,那么会在函数中打印输出。默认值: True
返回值:
最大内存消耗的树图
示例代码:
>>> from jittor.models import resnet18
>>> import jittor as jt
>>> net = resnet18()
>>> with jt.flag_scope(trace_py_var=3, profile_memory_enable=1):
... imgs = jt.randn((1,3,224,224))
... net(imgs).sync()
... jt.get_max_memory_treemap()
...
|
├─./python/jittor/test/test_memory_profiler.py:100(test_sample)
| [19.03 MB; 29.67%]
| ./python/jittor/test/test_memory_profiler.py:100
| |
| └─./python/jittor/__init__.py:730(__call__)
| [19.03 MB; 29.67%]
| ./python/jittor/__init__.py:730
| |
| └─./python/jittor/models/resnet.py:152(execute)
| [19.03 MB; 29.67%]
| ./python/jittor/models/resnet.py:152
| |
| ├─./python/jittor/models/resnet.py:142(_forward_impl)
| | [6.13 MB; 9.55%]
| | ./python/jittor/models/resnet.py:142
| | |
'''
div1 = "[!@#div1!@#]"
div2 = "[!@#div2!@#]"
div3 = "[!@#div3!@#]"
info = jt.get_max_memory_info()
vars = []
vars_ = info.split(div1)
max_memory_size = int(vars_[0])
vars_ = vars_[1:]
for v_ in vars_:
v__ = v_.split(div2)
vinfo = v__[0].split("{")[0]
var = {'size':int(v__[1]), 'stack':[], 'cnt':1, "vinfo":vinfo}
v__ = v__[2:-1]
for s_ in v__:
s__ = s_.split(div3)
s = {'path':s__[0], 'name':s__[1], 'type':s__[2]}
var['stack'].append(s)
vars.append(var)
if (build_by == 0): # build tree by name
tree = {'name':'root', "children":[], 'size':0, 'cnt':1, 'path':[], 'type':'', 'vinfo':[]}
def find_child(now, key):
for c in now['children']:
if (c['name'] == key):
return c
return None
for v in vars:
now = tree
now['size'] += v['size']
now['cnt'] += v['cnt']
now['vinfo'].append(v['vinfo'])
for s in v['stack']:
ch = find_child(now, s['name'])
if (ch is not None):
if (not s['path'] in ch['path']):
ch['path'].append(s['path'])
assert(ch['type']==s['type'])
now = ch
now['size'] += v['size']
now['cnt'] += v['cnt']
now['vinfo'].append(v['vinfo'])
else:
now_ = {'name':s['name'], "children":[], 'size':v['size'], 'cnt':v['cnt'], 'path':[s['path']], 'type':s['type'], 'vinfo':[v['vinfo']]}
now['children'].append(now_)
now = now_
elif (build_by == 1): # build tree by path
tree = {'name':'root', "children":[], 'size':0, 'cnt':0, 'path':'_root_', 'type':'', 'vinfo':[]}
def find_child(now, key):
for c in now['children']:
if (c['path'] == key):
return c
return None
for v in vars:
now = tree
now['size'] += v['size']
now['cnt'] += v['cnt']
now['vinfo'].append(v['vinfo'])
for s in v['stack']:
ch = find_child(now, s['path'])
if (ch is not None):
now = ch
now['size'] += v['size']
now['cnt'] += v['cnt']
now['vinfo'].append(v['vinfo'])
else:
now_ = {'name':s['name'], "children":[], 'size':v['size'], 'cnt':v['cnt'], 'path':s['path'], 'type':s['type'], 'vinfo':[v['vinfo']]}
now['children'].append(now_)
now = now_
else:
assert(False)
def sort_tree(now):
def takeSize(elem):
return elem['size']
now['children'].sort(key=takeSize, reverse=True)
for c in now['children']:
sort_tree(c)
sort_tree(tree)
out = print_tree(tree, max_memory_size, '', '', build_by)
if (do_print):
print(out)
return tree, out
[文档]
def python_pass_wrapper(mod_func, args, kw):
'''
使用给定的模块函数 ``mod_func`` 、参数 ``args`` 和关键字参数 ``kw`` 通过 Python 的
``importlib`` 导入模块函数并执行它,最后返回执行结果。
参数:
- mod_func (``str``): 完全限定名称的模块函数,例如 ``numpy.matmul``。
- args (``Tuple[str]``): 需要传递给模块函数的参数,这些参数应以元组的形式提供,元组中的每一项都要是字符串。 例如,如果函数需要两个参数 a 和 b,调用应该为 ``python_pass_wrapper('func', ('a', 'b'), kw)``
- kw (``dict``): 需要传递给模块函数的关键字参数,应以字典的形式提供。
代码示例:
>>> jt.python_pass_wrapper('math.sqrt', ('25',), {})
5
>>> jt.python_pass_wrapper('builtins.len', ('[1, 2, 3]',), {})
3
返回值:
Any: 返回模块函数调用的结果。
'''
import importlib
mod, func = mod_func.rsplit(".", 1)
mod = importlib.import_module(mod)
func = getattr(mod, func)
args = args + ("**kw",)
args = ",".join(args)
return eval(f"func({args})")
# def auto_parallel(n, src, block_num=1024, **kw):
# """
# auto parallel(CPU and GPU) n-d for loop function like below:
# Before:
# void inner_func(int n0, int i0, int n1, int i1) {
# ...
# }
# for (int i0=0; i0<n0; i0++)
# for (int i1=0; i1<n1; i1++)
# inner_func(n0, i0, n1, i1, ...);
# After:
# @python.jittor.auto_parallel(2)
# void inner_func(int n0, int i0, int n1, int i1) {
# ...
# }
# inner_func(n0, 0, n1, 0, ...);
# """
# # src = prev_func func_name(args)code
# a, b = src.split('(', 1)
# prev_func, func_name = a.rsplit(None, 1)
# args, code = b.split(')', 1)
# args = args.split(',')
# assert len(args) >= n*2, (args, n)
# oargs = args[n*2:]
# pargs = args[:n*2]
# piargs = pargs[1::2]
# pnargs = pargs[0::2]
# pnargs2 = [ a.split()[-1] for a in pnargs ]
# oargs2 = [ a.split()[-1] for a in oargs ]
# entry_func_args_def = ",".join(["int tn"+str(i) for i in range(n)]
# + pnargs + oargs)
# entry_func_args = ",".join(["tn"+str(i) for i in range(n)]
# + pnargs2 + oargs2)
# tid_def = ""
# tid_loop = ""
# call_args = []
# for i in reversed(range(n)):
# tid_def += f" auto tid{i} = tid & ((1<<tn{i})-1);"
# tid_def += f" auto tnum{i} = 1<<tn{i};"
# tid_def += f" tid = tid>>tn{i};"
# for i in range(n):
# tid_loop += f" for (int i{i}=tid{i}; i{i}<{pnargs2[i]}; i{i}+=tnum{i})"
# call_args.append(pnargs2[i])
# call_args.append(f"i{i}")
# call_args += oargs2
# call_args = ",".join(call_args)
# xn = ' '
# new_src = f"""
# #ifdef JIT_cuda
# __device__
# #endif
# {src.replace(func_name, func_name+"_inner", 1)}
# #ifdef JIT_cuda
# __global__ static void {func_name}_entry({entry_func_args_def}) {{
# int tid = threadIdx.x + blockIdx.x * blockDim.x;
# {tid_def}
# {tid_loop}
# {func_name}_inner({call_args});
# }}
# #endif
# inline static void {func_name}({",".join(pargs+oargs)}) {{
# #ifdef JIT_cuda
# int thread_num = 256*{block_num};
# {xn.join([f"int tn{i} = NanoVector::get_nbits(std::min(thread_num, {pnargs2[i]})) - 2;thread_num >>= tn{i};" for i in reversed(range(n))])}
# thread_num = 1<<({"+".join([f"tn{i}" for i in range(n)])});
# int p1 = std::max(thread_num/{block_num}, 1);
# int p2 = std::min(thread_num, {block_num});
# {func_name}_entry<<<p1,p2>>>({entry_func_args});
# #else
# {xn.join([f"for (int i{i}=0; i{i}<{pnargs2[i]}; i{i}++)" for i in range(n)])}
# {func_name}_inner({call_args});
# #endif
# }}
# """
# return new_src
[文档]
def numpy_cumprod(a, dim):
'''
沿着指定维度计算输入张量在这个维度上的累乘,基于numpy实现。
累乘是指,在指定方向上:
.. math::
y_i=x_1 \\times x_2 \\times x_3 \\times \\cdots \\times x_i
参数:
- a (``Var``): 输入张量 Var, 任意形状
- dim (``int``): 指定进行乘法的轴。默认为最后一维。
代码示例:
>>> a = jt.arange(-3, 6).reshape(3, 3)
jt.Var([[-3 -2 -1]
[ 0 1 2]
[ 3 4 5]], dtype=int32)
>>> jt.numpy_cumprod(a, 1)
jt.Var([[-3 6 -6]
[ 0 0 0]
[ 3 12 60]], dtype=int32)
>>> jt.numpy_cumprod(a, 0)
jt.Var([[ -3 -2 -1]
[ 0 -2 -2]
[ 0 -8 -10]], dtype=int32)
返回值:
``Var``,输出的张量,累乘后的结果,形状和输入一致
'''
class CumprodFunc(jt.Function):
def forward_code(self, np, data):
a = data["inputs"][0]
b = data["outputs"][0]
out = np.cumprod(a, self.dim)
np.copyto(b, out)
def backward_code(self, np, data):
a, b, dout = data["inputs"]
out = data["outputs"][0]
sdim = a.shape[self.dim]
dim = (len(a.shape)+1)*[1]
dim[self.dim+1] = sdim
res = np.tile(np.expand_dims(b, self.dim+1), dim)
dout = np.tile(np.expand_dims(dout, self.dim+1), dim)
dim[self.dim]=sdim
dim[self.dim+1]=1
a = np.tile(np.expand_dims(a, self.dim), dim)
res = res/a
mask = np.tril(np.ones((sdim, sdim)))
for i in range(self.dim):
mask = np.expand_dims(mask, 0)
for i in range(len(a.shape)-self.dim-2):
mask = np.expand_dims(mask, -1)
res = np.sum(mask*res*dout, self.dim)
np.copyto(out, res)
def execute(self, a, dim):
self.save_vars = a
self.dim = dim
self.res = jt.numpy_code(
a.shape,
a.dtype,
[a],
self.forward_code,
)
return self.res
def grad(self, grad_a):
a = self.save_vars
b = self.res
return jt.numpy_code(
a.shape,
a.dtype,
[a, b, grad_a],
self.backward_code,
)
func = CumprodFunc()
if dim<0:
dim+=len(a.shape)
return func(a, dim)
[文档]
def linspace(start, end, steps):
'''
生成一个由 ``start`` 到 ``end`` 等距的一维张量,其中共有 ``steps`` 个元素,并且包含end。也就是说输出张量为:
.. math::
\\left(\\text { start, } \\text { start }+\\frac{\\text { end }- \\text { start }}{\\text { steps }-1}, \\ldots, \\text { start }+(\\text { steps }-2) * \\frac{\\text { end }- \\text { start }}{\\text { steps }-1}, \\text { end }\\right)
参数:
- start (int, float): 起始值
- end (int, float): 结束值
- steps (int): 构造的 Var 的长度
代码示例:
>>> jt.linspace(3, 10, 5)
jt.Var([ 3. 4.75 6.5 8.25 10. ], dtype=float32)
返回值:
Var,一个一维张量,包含在 ``start`` 和 ``end`` 之间的等间隔步数。
'''
if steps > 1:
res = jt.index((steps,))[0]
res = res*float((end-start)/(steps-1))+start
else:
res = jt.array([start])
return res
[文档]
def randperm(n, dtype="int32"):
'''
生成一个随机排列张量 ``Var``,其中元素为从 ``0`` 到 ``n - 1`` 这 ``n`` 个整数。
参数:
- n (``int``): 要生成的随机排列数组的长度。
- dtype (``str``): 返回的张量中的数据类型,默认为 ``int32``
代码示例:
>>> print(jt.randperm(7))
jt.Var([5 3 6 0 4 2 1], dtype=int32)
返回值:
一个从 ``0`` 到 ``n-1`` 的随机排列的 ``Var`` 对象。
'''
key = jt.random((n,))
index, _ = jt.argsort(key)
return index.cast(dtype)
[文档]
def set_global_seed(seed, different_seed_for_mpi=True):
'''设置Python,numpy和jittor的随机数生成器的种子。
这个函数同步地设置了这三个随机数生成器的种子,将它们设置为同一个值,以保证在使用这些生成器时获得一致的随机数。
Jittor还确保了jittor.dataset.Dataset的每一个工作进程都持有一个不同的种子,同时也保证了使用mpi的每一个进程都持有一个不同的种子。这是通过如下的公式实现的:
.. code-block:: python
(global_seed ^ (worker_id * 1167)) ^ 1234 + jt.rank * 2591
参数:
- seed (``int``): 指定要设置的随机数种子的值,无默认值
- different_seed_for_mpi (``bool``): 是否为每一个使用mpi的进程设置一个不同的种子,默认为True。
'''
if (different_seed_for_mpi):
seed = seed + jt.rank * 2591
import random
random.seed(seed)
jt.set_seed(seed)
np.random.seed(seed)
try:
import cupy
cupy.random.seed(seed)
except:
pass
import time
set_global_seed(int(time.time() * 1000000) % 100000007)
[文档]
def searchsorted(sorted, values, right=False):
'''
对有序张量 ``sorted`` 的最后一个维度进行搜索以查找 ``values`` 中每个值应该插入到(插入后仍保持数组有序)的索引位置。
参数:
- sorted (``Var``): 输入的有顺序的数组
- values (``Var``): 要进行插入的值
- right (``bool``): 默认为 ``False``。
如果为 ``False``,则返回的索引 ``i`` 满足 ``sorted[i-1] < value <= sorted[i]``;如果为 ``True``,则返回的索引 ``i`` 满足 ``sorted[i-1] <= value < sorted[i]``。
返回值:
返回一个形状与 ``values`` 相同的 Var ,数组中的每个值表示对应的 ``values`` 在 ``sorted`` 中的索引位置。
代码示例:
>>> sorted = jt.array([[1, 3, 5, 7, 9], [2, 4, 6, 8, 10]])
>>> values = jt.array([[3, 6, 9], [3, 6, 9]])
>>> ret = jt.searchsorted(sorted, values)
>>> assert (ret == [[1, 3, 4], [1, 2, 4]]).all(), ret
>>> ret = jt.searchsorted(sorted, values, right=True)
>>> assert (ret == [[2, 3, 5], [1, 3, 4]]).all(), ret
>>> sorted_1d = jt.array([1, 3, 5, 7, 9])
>>> ret = jt.searchsorted(sorted_1d, values)
>>> assert (ret == [[1, 3, 4], [1, 3, 4]]).all(), ret
'''
_searchsorted_header = f"""
namespace jittor {{
@python.jittor.auto_parallel(2)
inline static void searchsorted(
int batch_num, int batch_id, int value_num, int value_id,
int sorted_num, int batch_stride,
{sorted.dtype}* __restrict__ sort_p, {values.dtype}* __restrict__ value_p,
int32* __restrict__ index_p) {{
int32 l = batch_id * batch_stride;
int32 r = l + sorted_num;
auto v = value_p[batch_id * value_num + value_id];
while (l<r) {{
int32 m = (l+r)/2;
if (sort_p[m] {"<=" if right else "<"} v)
l = m+1;
else
r = m;
}}
index_p[batch_id * value_num + value_id] = l - batch_id * batch_stride;
}}
}}
"""
_searchsorted_src = """
int value_num = in1->shape[in1->shape.size()-1];
int sorted_num = in0->shape[in0->shape.size()-1];
int32 batch_num = in0->num / sorted_num;
int32 batch_num2 = in1->num / value_num;
int32 batch_stride = batch_num == 1 ? 0 : sorted_num;
CHECK(batch_num == batch_num2 || batch_num == 1);
searchsorted(batch_num2, 0, value_num, 0, sorted_num, batch_stride, in0_p, in1_p, out0_p);
"""
return jt.code(values.shape, "int32", [sorted, values],
cpu_header=_searchsorted_header,
cpu_src=_searchsorted_src,
cuda_header=_searchsorted_header,
cuda_src=_searchsorted_src)
[文档]
def scatter(x:jt.Var, dim:int, index:jt.Var, src:jt.Var, reduce='void'):
'''
根据指定的索引 ``index`` 将 ``src`` 中的元素散布到输入数组 ``x`` 的 ``dim`` 维度,返回修改后的 ``x`` 数组。
当 ``x`` 是3维数组时,该函数的效果可表述为:
.. code-block:: python
self[index[i][j][k]][j][k] = src[i][j][k] # if dim == 0
self[i][index[i][j][k]][k] = src[i][j][k] # if dim == 1
self[i][j][index[i][j][k]] = src[i][j][k] # if dim == 2
参数:
- x (``Var``): 输入数组
- dim (``int``): 指定的维度,用于指定沿哪个轴进行索引
- index (``Var``): 用于散布元素的索引,可以为空,或与 ``src`` 的维数相同。当索引为空时,该操作返回的是原始的 ``x`` 数组,即没有做任何修改。
- src (``Var``): 需要散布的源元素
- reduce (``str``,可选): 确定如何执行散布操作。此参数可以是 ``add`` 或 ``multiply``。
代码示例:
>>> src = jt.arange(1, 11).reshape((2, 5))
>>> index = jt.array([[0, 1, 2, 0]])
>>> x = jt.zeros((3, 5), dtype=src.dtype)
>>> jt.scatter(x, 0, index, src)
jt.Var([[1 0 0 4 0]
[0 2 0 0 0]
[0 0 3 0 0]], dtype=int32)
返回值:
Var,修改后的 ``x`` 数组,形状与其一致
'''
shape = index.shape
if src.shape != shape and src.numel() != 1:
src = src[tuple( slice(None,s) for s in shape )]
indexes = [ f'i{i}' for i in range(len(shape)) ]
indexes[dim] = index
return x.setitem(tuple(indexes), src, reduce)
def scatter_(x, dim, index, src, reduce='void'):
return x.assign(x.scatter(dim, index, src, reduce))
jt.Var.scatter = scatter
jt.Var.scatter_ = scatter_
[文档]
def gather(x, dim, index):
''' 该函数用给定的索引数组重新索引源数组(如果源数组是3-D数组)。
对于索引数组中的每个元素,假设当前元素的坐标是(i,j,k),则在结果数组中当前索引对应的元素取决于参数 ``dim`` 的值:
- 如果dim == 0, out[i][j][k] = input[index[i][j][k]][j][k]
- 如果dim == 1, out[i][j][k] = input[i][index[i][j][k]][k]
- 如果dim == 2, out[i][j][k] = input[i][j][index[i][j][k]]
参数:
- x (Var): 输入数组
- dim (int): 索引操作进行的轴
- index (Var): 元素的索引数组
代码示例:
>>> t = jt.array([[1, 2], [3, 4]])
>>> jt.gather(t, 1, jt.array([[0, 0], [1, 0]]))
jt.Var([[1 1]
[4 3]], dtype=int32)
>>> jt.gather(t, 0, jt.array([[0, 0], [1, 0]]))
jt.Var([[1 2]
[3 2]], dtype=int32)
返回值:
Var,通过指定的索引方式重新索引源数组,汇总得到的结果。
'''
shape = index.shape
indexes = [ f'i{i}' for i in range(len(shape)) ]
indexes[dim] = index
return x.getitem(tuple(indexes))
jt.Var.gather = gather
[文档]
def roll(x, shifts, dims=None):
'''
将给定的张量在指定维度上滚动,或者说循环移动 ``shifts`` 位。
参数:
- x (Var): 输入的张量
- shifts (``tuple[int]``): 滚动的偏移量
- dims (``tuple[int]``, 可选): 滚动的维度,默认为最后一维
代码示例:
>>> x = jt.array([1, 2, 3, 4, 5, 6, 7, 8]).view(4, 2)
>>> jt.roll(x, 1, 0)
jt.Var([[7 8]
[1 2]
[3 4]
[5 6]], dtype=int32)
>>> jt.roll(x, -2, 0)
jt.Var([[5 6]
[7 8]
[1 2]
[3 4]], dtype=int32)
返回值:
Var,循环移位后的张量
'''
if isinstance(shifts, int):
shifts = (shifts,)
if dims is None:
dims = tuple(range(len(shifts)))
elif isinstance(dims, int):
dims = (dims,)
assert len(dims) == len(shifts)
ids = [ f'i{i}' for i in range(x.ndim) ]
for i in range(len(dims)):
shift = shifts[i]
d = dims[i]
size = x.shape[d]
shift = shift % size
if shift<0: shift += size
ids[d] = f'(i{d}<{shift}?i{d}+{size-shift}:(i{d}-{shift}))'
return x.reindex(x.shape, ids)
jt.Var.roll = roll
[文档]
def safe_log(x):
'''
计算输入矩阵 x 的对数值。
.. math::
y_i=\\ln\\left(x_i\\right)
该函数在执行对数运算之前,使用 ``jt.safe_clip`` 将 x 的值限制在一个安全范围内 ``(1e-30, 1e+30)``,以避免因取对数时出现零或极大值造成计算错误。
参数:
- x (``Var``): 输入变量
返回值:
Var,``x`` 中元素的对数值,与 ``x`` 的形状相同。
代码示例:
>>> x = jt.array([0, 0.1, 1.0, 10.0, 100.0, -123])
>>> jt.safe_log(x)
jt.Var([-69.07755 -2.3025851 0. 2.3025851 4.6051702 -69.07755], dtype=float32)
'''
return jt.safe_clip(x, 1e-30, 1e30).log()
jt.Var.safe_log = safe_log
class _CTCLossFunction(jt.Function):
'''
这是一个在Jittor深度学习框架中实现CTC(Connectionist Temporal Classification)损失函数计算的类。本类主要用于实现RNN(如LSTM和GRU)在序列标记任务(如语音识别、手写识别)中的CTC损失计算。目标序列可能有不同的长度,且不需要对齐。
``execute()`` 参数:
- log_probs (jt.Var): 形状为(T, N, C)的张量,其中T是最大的输入长度,N是批处理大小,C是类别数量(包括空白符)。log_probs 应该是输入序列的负对数概率,它可以在接近零的地方取对数,防止数值下溢。元素类型为float
- targets (jt.Var): 形状为(N, S)的张量,其中S是最大的目标长度,包含在[0, C-1] 范围内的目标类别。在这种二维形式中,目标不需要被前后填充。元素类型为int
- input_lengths (jt.Var): 形状为(N,)的张量,包含每个输入序列的长度。元素类型为int
- target_lengths (jt.Var): 形状为(N,)的张量,包含每个目标序列的长度。元素类型为int
- blank (int, 可选): 空白符的标记。默认值: 0
- zero_infinity (bool, 可选): 如果设置为True,nll_loss将用'-inf'代替'inf'。默认值: False
``execute()`` 返回值:
res(jt.Var): 完成CTC之后的结果张量,形状为(N,)
代码示例:
>>> import jittor as jt
>>> CTC_loss_func = jt.misc._CTCLossFunction()
>>> log_probs = jt.randn(10, 100, 3)
>>> targets = jt.randn(100, 3)
>>> input_lengths = jt.randn(100)
>>> target_lengths = jt.randn(100)
>>> CTC_loss_func(log_probs, targets, input_lengths, target_lengths).shape
[100,]
'''
def execute(self, log_probs, targets, input_lengths, target_lengths, blank=0, zero_infinity=False):
self.blank = blank
T, N, C = log_probs.shape
_N, S = targets.shape
assert _N == N
log_alpha = jt.full([T,N,S*2+1], -1e30)
result = jt.empty((N,))
jt.code([log_probs, targets, input_lengths, target_lengths], [log_alpha, result], cpu_src=f"""
constexpr int blank = {blank};
for (int i=0; i<in0_shape1; i++) {{
int input_len = @in2(i);
int target_len = @in3(i);
@out0(0,i,0) = @in0(0,i,blank);
if (target_len)
@out0(0,i,1) = @in0(0,i,@in1(i,0));
for (int j=1; j<input_len; j++)
for (int k=0; k<target_len*2+1; k++) {{
int target = k%2 ? @in1(i,k/2) : blank;
int target_2 = target;
if (k>1 && k%2) target_2 = @in1(i,k/2-1);
out_type l1 = @out0(j-1,i,k);
out_type l2 = -1e30;
if (k>0) l2 = @out0(j-1,i,k-1);
out_type l3 = -1e30;
if (k>1 && target_2 != target)
l3 = @out0(j-1,i,k-2);
out_type m = std::max(l1, std::max(l2, l3));
@out0(j,i,k) = std::log(
std::exp(l1-m) +
std::exp(l2-m) +
std::exp(l3-m)
) + m + @in0(j,i,target);
}}
if (input_len==0)
@out1(i) = @out0(0,i,0);
else {{
out_type l1 = @out0(input_len-1, i, target_len*2);
out_type l2 = -1e30;
if (target_len)
l2 = @out0(input_len-1, i, target_len*2-1);
out_type m = std::max(l1, l2);
out_type log_likelihood = std::log(std::exp(l1-m)+std::exp(l2-m))+m;
@out1(i) = -log_likelihood;
}}
}}
""", cuda_src=f"""
__global__ void kernel(@ARGS_DEF) {{
@PRECALC;
constexpr int blank = {blank};
for (int i=blockIdx.x; i<in0_shape1; i+=gridDim.x) {{
int input_len = @in2(i);
int target_len = @in3(i);
@out0(0,i,0) = @in0(0,i,blank);
if (target_len)
@out0(0,i,1) = @in0(0,i,@in1(i,0));
for (int j=1; j<input_len; j++)
for (int k=threadIdx.x; k-threadIdx.x<target_len*2+1; k+=blockDim.x) {{
__syncthreads();
if (k>=target_len*2+1)
continue;
int target = k%2 ? @in1(i,k/2) : blank;
int target_2 = target;
if (k>1 && k%2) target_2 = @in1(i,k/2-1);
out_type l1 = @out0(j-1,i,k);
out_type l2 = -1e30;
if (k>0) l2 = @out0(j-1,i,k-1);
out_type l3 = -1e30;
if (k>1 && target_2 != target)
l3 = @out0(j-1,i,k-2);
out_type m = ::max(l1, ::max(l2, l3));
@out0(j,i,k) = ::log(
::exp(l1-m) +
::exp(l2-m) +
::exp(l3-m)
) + m + @in0(j,i,target);
}}
__syncthreads();
if (input_len==0)
@out1(i) = @out0(0,i,0);
else {{
out_type l1 = @out0(input_len-1, i, target_len*2);
out_type l2 = -1e30;
if (target_len)
l2 = @out0(input_len-1, i, target_len*2-1);
out_type m = ::max(l1, l2);
out_type log_likelihood = ::log(::exp(l1-m)+::exp(l2-m))+m;
@out1(i) = -log_likelihood;
}}
}}
}}
kernel<<<std::min(in0_shape1, 1024), std::min(in1_shape1*2+1, 1024)>>>(@ARGS);
""")
self.saved_var = [log_probs, targets, input_lengths, target_lengths, log_alpha, result]
return result
def grad(self, dout):
blank = self.blank
inputs = self.saved_var + [dout]
dlog_probs = jt.zeros_like(inputs[0])
dlog_alpha = jt.zeros_like(inputs[4])
jt.code(inputs, [dlog_probs, dlog_alpha], cpu_src=f"""
constexpr int blank = {blank};
for (int i=0; i<in0_shape1; i++) {{
int input_len = @in2(i);
int target_len = @in3(i);
if (input_len==0)
// write out1 --> read in6
// out1(i) = out0(0,i,0);
@out1(0,i,0) = @in6(i);
else {{
out_type l1 = @in4(input_len-1, i, target_len*2);
out_type l2 = -1e30;
if (target_len)
l2 = @in4(input_len-1, i, target_len*2-1);
out_type m = std::max(l1, l2);
// out_type log_likelihood = std::log(std::exp(l1-m)+std::exp(l2-m))+m;
// out1(i) = -log_likelihood;
out_type l1_exp = std::exp(l1-m);
out_type l2_exp = std::exp(l2-m);
out_type sumexp = l1_exp + l2_exp;
out_type dlog_likelihood = -@in6(i);
out_type dl1 = dlog_likelihood * l1_exp / sumexp;
out_type dl2 = dlog_likelihood * l2_exp / sumexp;
@out1(input_len-1, i, target_len*2) = dl1;
if (target_len)
@out1(input_len-1, i, target_len*2-1) = dl2;
}}
for (int j=input_len-1; j>0; j--)
for (int k=0; k<target_len*2+1; k++) {{
int target = k%2 ? @in1(i,k/2) : blank;
int target_2 = target;
if (k>1 && k%2) target_2 = @in1(i,k/2-1);
out_type l1 = @in4(j-1,i,k);
out_type l2 = -1e30;
if (k>0) l2 = @in4(j-1,i,k-1);
out_type l3 = -1e30;
if (k>1 && target_2 != target)
l3 = @in4(j-1,i,k-2);
out_type m = std::max(l1, std::max(l2, l3));
out_type l1_exp = std::exp(l1-m);
out_type l2_exp = std::exp(l2-m);
out_type l3_exp = std::exp(l3-m);
out_type sumexp = l1_exp + l2_exp + l3_exp;
out_type dalpha = @out1(j,i,k);
@out0(j,i,target) += dalpha;
@out1(j-1,i,k) += dalpha * l1_exp / sumexp;
if (k>0)
@out1(j-1,i,k-1) += dalpha * l2_exp / sumexp;
if (k>1 && target_2 != target)
@out1(j-1,i,k-2) += dalpha * l3_exp / sumexp;
}}
// read in0 -> white out0
// write out0 ->read out1
// out0(0,i,0) = in0(0,i,blank);
@out0(0,i,blank) += @out1(0,i,0);
if (target_len)
@out0(0,i,@in1(i,0)) += @out1(0,i,1);
}}
""", cuda_src=f"""
__global__ void kernel(@ARGS_DEF) {{
@PRECALC;
constexpr int blank = {blank};
for (int i=blockIdx.x; i<in0_shape1; i+=gridDim.x) {{
int input_len = @in2(i);
int target_len = @in3(i);
if (input_len==0)
// write out1 --> read in6
// out1(i) = out0(0,i,0);
@out1(0,i,0) = @in6(i);
else {{
out_type l1 = @in4(input_len-1, i, target_len*2);
out_type l2 = -1e30;
if (target_len)
l2 = @in4(input_len-1, i, target_len*2-1);
out_type m = ::max(l1, l2);
// out_type log_likelihood = ::log(::exp(l1-m)+::exp(l2-m))+m;
// out1(i) = -log_likelihood;
out_type l1_exp = ::exp(l1-m);
out_type l2_exp = ::exp(l2-m);
out_type sumexp = l1_exp + l2_exp;
out_type dlog_likelihood = -@in6(i);
out_type dl1 = dlog_likelihood * l1_exp / sumexp;
out_type dl2 = dlog_likelihood * l2_exp / sumexp;
@out1(input_len-1, i, target_len*2) = dl1;
if (target_len)
@out1(input_len-1, i, target_len*2-1) = dl2;
}}
for (int j=input_len-1; j>0; j--)
for (int k=threadIdx.x; k-threadIdx.x<target_len*2+1; k+=blockDim.x) {{
__syncthreads();
if (k>=target_len*2+1)
continue;
int target = k%2 ? @in1(i,k/2) : blank;
int target_2 = target;
if (k>1 && k%2) target_2 = @in1(i,k/2-1);
out_type l1 = @in4(j-1,i,k);
out_type l2 = -1e30;
if (k>0) l2 = @in4(j-1,i,k-1);
out_type l3 = -1e30;
if (k>1 && target_2 != target)
l3 = @in4(j-1,i,k-2);
out_type m = ::max(l1, ::max(l2, l3));
out_type l1_exp = ::exp(l1-m);
out_type l2_exp = ::exp(l2-m);
out_type l3_exp = ::exp(l3-m);
out_type sumexp = l1_exp + l2_exp + l3_exp;
out_type dalpha = @out1(j,i,k);
atomicAdd(&@out0(j,i,target), dalpha);
atomicAdd(&@out1(j-1,i,k), dalpha * l1_exp / sumexp);
if (k>0)
atomicAdd(&@out1(j-1,i,k-1), dalpha * l2_exp / sumexp);
if (k>1 && target_2 != target)
atomicAdd(&@out1(j-1,i,k-2), dalpha * l3_exp / sumexp);
}}
// read in0 -> white out0
// write out0 ->read out1
// out0(0,i,0) = in0(0,i,blank);
__syncthreads();
if (threadIdx.x==0) {{
@out0(0,i,blank) += @out1(0,i,0);
if (target_len)
@out0(0,i,@in1(i,0)) += @out1(0,i,1);
}}
}}
}}
kernel<<<std::min(in0_shape1, 1024), std::min(in1_shape1*2+1, 1024)>>>(@ARGS);
""")
return (dlog_probs,)
[文档]
def ctc_loss(log_probs, targets, input_lengths, target_lengths, blank=0, reduction='mean', zero_infinity=False):
'''完成连通时间分类 (CTC, Connectionist Temporal Classification) 损失的计算。
计算连续(未分段)时间序列与目标序列之间的损失。CTCLoss 对输入到目标的可能对齐的概率求和,生成一个损失值,该值对每个输入节点可微分。假定输入到目标的对齐是“多对一”的,这限制了目标序列的长度,使其必须小于等于输入长度。
参数:
- log_probs (Var): 形状为[T, N, C]的张量,T为序列长度,N为批次大小,C为类别数量。
- targets (Var): 形状为[N, S]的张量,N为批次大小,S为目标序列长度,其中的元素应在[0,C)范围内。
- input_lengths (Var): 形状为[N]的张量,每个元素代表输入序列的长度,元素应在[0,T]范围内。
- target_lengths (Var): 形状为[N]的张量,每个元素代表目标序列的长度,元素应在[0,S]范围内。
- blank (int): 默认为 0,空白标签索引
- reduction (str):减少批量损失,如果reduction是none,它会返回一个(N,)的数组,如果reduction是mean或sum,它会返回一个标量。默认为 "mean"。
- zero_infinity (bool): 默认为 False, 用于梯度计算
代码示例:
.. code-block:: python
import jittor as jt
T = 50 # Input sequence length
C = 20 # Number of classes (including blank)
N = 16 # Batch size
S = 30 # Target sequence length of longest target in batch (padding length)
S_min = 10 # Minimum target length, for demonstration purposes
input = jt.randn(T, N, C).log_softmax(2)
# Initialize random batch of targets (0 = blank, 1:C = classes)
target = jt.randint(low=1, high=C, shape=(N, S), dtype=jt.int)
input_lengths = jt.full((N,), T, dtype=jt.int)
target_lengths = jt.randint(low=S_min, high=S+1, shape=(N,), dtype=jt.int)
loss = jt.ctc_loss(input, target, input_lengths, target_lengths)
# calculation result, loss is: jt.Var([115.42529], dtype=float32)
dinput = jt.grad(loss, input)
返回值:
Var,包含一个数,是计算出的 CTC 值。
'''
result = _CTCLossFunction.apply(log_probs, targets, input_lengths, target_lengths, blank, zero_infinity)
if reduction=="mean":
return result.mean()
elif reduction=="sum":
return result.sum()
assert reduction=="none"
return result
[文档]
class CTCLoss(jt.Module):
'''
计算链接主义时序分类 (Connectionist Temporal Classification) 损失函数。
计算一个连续时间序列和目标差别的损失函数,刻画输入与目标的对应。CTCLoss 对输入到目标的各个对齐方式的概率求和,得到一个对每个输入可微分的损失值。输入到目标的对齐应该是“多对一的”,因此目标序列长度被限制为不超过输入序列长度。
参考文献:
A. Graves et al.: Connectionist Temporal Classification: Labelling Unsegmented Sequence Data with Recurrent Neural Networks:
https://www.cs.toronto.edu/~graves/icml_2006.pdf
参数:
- blank (int): 空标签的编号。默认值:0
- reduction (string): 如何聚合所有批次的计算结果,mean 是均值,sum 是求和,none 是不聚合全部返回。默认值: ``'mean'``
- zero_infinity (bool): 将损失函数中的无穷大项和对应梯度置零(这种情况一般是在输入短于目标时出现)。默认值:False
形状:
- Input:
- log_probs: :math:`(T, N, C)`,其中 :math:`T` 为序列长度,:math:`N` 为批次大小,:math:`S` 为分类个数,表示输出序列的每项被分类到各个类的概率的对数。
- targets: :math:`(N, S)`,其中 :math:`N` 为批次大小,:math:`S` 为目标序列长度,表示目标序列每项的分类序号,范围 :math:`[0, C)`
- input_lengths: :math:`(N)`,其中 :math:`N` 为批次大小,表示每个输入的实际长度,范围 :math:`[0, T]`
- target_lengths: :math:`(N)`,其中 :math:`N` 为批次大小,表示每个目标序列的实际长度,范围 :math:`[0, S]`
- Output: 如果 reduction 为 ``'mean'`` (默认)或 ``'sum'`` 则为一标量;如果 reduction 为 ``'none'`` 则为 :math:`(N,)`,其中 :math:`N` 为批次大小。
代码示例:
>>> T, C, N, S = 50, 20, 16, 30
>>> S_min = 10 # 最小目标长度,用于演示目的
>>> input = jt.randn(T, N, C).log_softmax(2)
>>> # 初始化随机批次的目标 (0 = 空白, 1:C = 类别)
>>> target = jt.randint(low=1, high=C, shape=(N, S), dtype=jt.int)
>>> input_lengths = jt.full((N,), T, dtype=jt.int)
>>> target_lengths = jt.randint(low=S_min, high=S+1, shape=(N,), dtype=jt.int)
>>> ctc_loss = jt.CTCLoss()
>>> ctc_loss(input, target, input_lengths, target_lengths)
jt.Var([114.68321], dtype=float32)
'''
def __init__(self, blank=0, reduction='mean', zero_infinity=False):
self.blank = blank
self.reduction = reduction
self.zero_infinity = zero_infinity
def execute(self, log_probs, targets, input_lengths, target_lengths):
return ctc_loss(log_probs, targets, input_lengths, target_lengths, self.blank, self.reduction, self.zero_infinity)
def _simple_for(x, func):
with jt.flag_scope(compile_options={"FLAGS: -O2 ":1}):
src = f'''
__inline_static__
@python.jittor.auto_parallel(1)
void kernel(int n0, int i0, in0_type* _x, out0_type* y) {{
using namespace std;
auto x = _x[i0];
y[i0] = {func};
}}
kernel(in0->num, 0, in0_p, out0_p);
'''
return jt.code(x.shape, "bool", [x], cpu_src=src, cuda_src=src)
[文档]
def isnan(x):
'''
检查输入 Var 中的元素是否为 NaN。
参数:
x (Var): 输入的张量。
代码示例:
>>> x = jt.array([1.0, float('nan'), 3])
>>> jt.isnan(x)
jt.Var([False True False], dtype=bool)
返回值:
Var, 返回一个与输入张量x同形状的张量,每个元素是判断x对应元素是否为NaN的结果。
'''
return _simple_for(x, "isnan(float(x))")
jt.Var.isnan = isnan
[文档]
def isfinite(x):
'''
检查输入 Var 中的元素是否为有限数值。
如果 ``x`` 中元素既不是NaN(Not a Number)也不是无穷大或无穷小(Infinity 或 -Infinity),则返回True,否则返回False。
参数:
x (Var): 输入的张量。
代码示例:
>>> x = jt.array([1.0, float('Infinity'), float('nan'), 3])
>>> jt.isfinite(x)
jt.Var([ True False False True], dtype=bool)
返回值:
Var, 返回一个与输入张量x同形状的张量,每个元素是判断x对应元素是否为有限数值的结果。
'''
return _simple_for(x, "!isnan(float(x)) && !isinf(float(x))")
jt.Var.isfinite = isfinite
[文档]
def isinf(x):
'''
检查输入 Var 中的元素是否为无穷大值。
如果 ``x`` 中元素是无穷大或无穷小(Infinity 或 -Infinity),则返回True,否则返回False。
参数:
x (Var): 输入的张量。
代码示例:
>>> x = jt.array([1.0, float('inf'), float('nan'), float('-inf'), 3])
>>> jt.isinf(x)
jt.Var([False True False True False], dtype=bool)
返回值:
Var, 返回一个与输入张量x同形状的张量,每个元素是判断x对应元素是否为无穷大值的结果。
'''
return _simple_for(x, "isinf(float(x))")
jt.Var.isinf = isinf
[文档]
def isneginf(x):
'''检查输入 Var 中的元素是否为负无穷。
如果 ``x`` 中元素是无穷小(-Infinity),则返回True,否则返回False。
参数:
x (Var): 输入的张量。
代码示例:
>>> x = jt.array([1.0, float('inf'), float('nan'), float('-inf'), 3])
>>> jt.isneginf(x)
jt.Var([False False False True False], dtype=bool)
返回值:
Var, 返回一个与输入张量x同形状的张量,每个元素是判断x对应元素是否为负无穷的结果。
'''
return _simple_for(x, "x<0 && isinf(float(x))")
jt.Var.isneginf = isneginf
[文档]
def isposinf(x):
'''
检查输入 Var 中的元素是否为正无穷。
如果 ``x`` 中元素是无穷大(+Infinity),则返回True,否则返回False。
参数:
x (Var): 输入的张量。
代码示例:
>>> x = jt.array([1.0, float('inf'), float('nan'), float('-inf'), 3])
>>> jt.isposinf(x)
jt.Var([False True False False False], dtype=bool)
返回值:
Var, 返回一个与输入张量x同形状的张量,每个元素是判断x对应元素是否为正无穷的结果。
'''
return _simple_for(x, "x>0 && isinf(float(x))")
jt.Var.isposinf = isposinf
# fake torch interface
[文档]
def contiguous(x):
'''在内存中创建给定 Var 的深拷贝。
该函数确保结果的内存是连续的,且与源张量分开。这在您需要更改张量,但不希望这些更改反映在原始张量中时非常有用。
参数:
x (Var): 输入的张量。
代码示例:
>>> x = jt.array([1,2,3])
>>> y = contiguous(x)
>>> y[0] = 5
>>> print(x)
jt.Var([1 2 3], dtype=int32)
>>> print(y)
jt.Var([5 2 3], dtype=int32)
返回值:
Var, ``x`` 的深拷贝,占用连续的内存
'''
return x.clone()
jt.Var.contiguous = contiguous
[文档]
def cpu(x):
'''将指定的 Var 复制到CPU。
参数:
x (Var): 输入的张量。
代码示例:
>>> a = jt.array([1, 2, 3])
>>> b = jt.cpu(a)
>>> print(b)
jt.Var([1 2 3], dtype=int32)
返回值:
Var, 与输入数据有相同值的新数据变量,位于 CPU 上。
'''
return x.clone()
jt.Var.cpu = cpu
[文档]
def to(x, *args, **kargs):
'''
将 Jittor变量 ``x`` 转换为指定类型或将其移动到指定计算设备上。
这个函数可以对Jittor变量进行类型转换(如,将整数类型转换为浮点数类型),还可以把数据在CPU和GPU设备之间移动(假设你的系统有GPU设备,且已经安装好了CUDA)。
参数:
- x (``Var``): 要转移或转换的 Jittor 变量。
- \\*args: 可变长度参数列表,第一个参数可以是:
- ``NanoString`` 或可调用对象:在这种情况下,``x`` 将被转换为指定的数据类型。
- ``str``: 指定设备的字符串('cuda' 或 'cpu')。如果字符串中包含 'cuda',Jittor 将使用 CUDA(GPU)进行未来的操作。如果字符串中包含 'cpu',Jittor 将使用 CPU。
- \\**kargs: 任意关键字参数。
返回值:
``Var``: 一个新的 Jittor 变量,已移至指定设备或转换为新的数据类型。如果未指定设备或数据类型,则返回 'x' 的克隆。
代码示例:
>>> var = jt.array([1, 2, 3])
>>> var_cuda = to(var, 'cuda') # 移动到 GPU
>>> var_float = to(var, jt.float32) # 转换为 float32
'''
if len(args) >= 1:
s = args[0]
if isinstance(s, jt.NanoString) or callable(s):
return x.cast(s)
s = str(s)
if "cuda" in s:
jt.flags.use_cuda = 1
elif "cpu" in s:
jt.flags.use_cuda = 0
return x.clone()
jt.Var.to = to
[文档]
def rsqrt(x):
'''
计算并返回 Jittor 变量的平方根的倒数。
此函数对给定的 Jittor 变量执行平方根的倒数运算。它等效于 :math:`\\displaystyle \\frac{1}{\sqrt x}`。
参数:
x (``Var``): 输入的 Jittor 变量。
返回值:
``Var``: 输入变量的平方根的倒数。返回结果的形状和数据类型与输入参数一致。
代码示例:
>>> var = jt.array([4, 9, 16])
>>> result = rsqrt(var)
>>> print(result)
[0.5, 0.33333333, 0.25] # 分别是 2, 3, 4 的平方根的倒数
'''
return 1/jt.sqrt(x)
jt.Var.rsqrt = rsqrt
[文档]
def from_torch(x):
'''
将PyTorch Tensor转化为Jittor Var。
参数:
x (torch.Tensor): 需要转化的PyTorch Tensor。
返回值:
jt.Var: 转化后的Jittor Variable。
代码示例:
>>> import torch
>>> import jittor as jt
>>> a = torch.tensor([1.0, 2.0, 3.0])
>>> b = from_torch(a)
>>> print(b)
jt.Var([1.0, 2.0, 3.0])
'''
return jt.Var(x.cpu().numpy())
[文档]
def triu(input: jt.Var, diagonal:int=0):
'''
返回输入的矩阵 (即2-D张量) 或者批量矩阵的上三角部分,其他元素被设置为0。
参数:
- input(``Var``): 输入张量。
- diagonal(``int``): 考虑的对角线,默认值为 ``0``。
返回值:
返回上三角的张量,类型为 ``Var`` .
代码示例:
>>> a = jt.ones(3, 3)
>>> b = jt.triu(a)
>>> assert jt.all_equal(b, [[1,1,1],[0,1,1],[0,0,1]])
>>> b = jt.triu(a, diagonal=1)
>>> assert jt.all_equal(b, [[0,1,1],[0,0,1],[0,0,0]])
>>> b = jt.triu(a, diagonal=-1)
>>> assert jt.all_equal(b, [[1,1,1],[1,1,1],[0,1,1]])
'''
index = input.index()
mask = index[-2] <= index[-1] - diagonal
return jt.ternary(mask, input, jt.zeros_like(input))
jt.Var.triu = triu
jt.Var.triu_ = lambda x: x.assign(x.triu())
[文档]
def tril(input: jt.Var, diagonal:int=0):
'''
返回一个矩阵(2-D张量)或批量矩阵输入的下三角部分,结果张量out的其它元素被设为0.
参数:
- input(``Var``): 输入张量。
- diagonal(``int``): 考虑的对角线,默认值为 ``0``。
返回值:
下三角矩阵,类型为 ``Var``。
代码示例:
>>> a = jt.ones(3, 3)
>>> b = jt.tril(a)
>>> assert jt.all_equal(b, [[1,0,0],[1,1,0],[1,1,1]])
>>> b = jt.tril(a, diagonal=1)
>>> assert jt.all_equal(b, [[1,1,0],[1,1,1],[1,1,1]])
>>> b = jt.tril(a, diagonal=-1)
>>> assert jt.all_equal(b, [[0,0,0],[1,0,0],[1,1,0]])
'''
index = input.index()
mask = index[-2] >= index[-1] - diagonal
return jt.ternary(mask, input, jt.zeros_like(input))
jt.Var.tril = tril
jt.Var.tril_ = lambda x: x.assign(x.tril())
[文档]
def all_equal(a: jt.Var, b: jt.Var):
'''
检查两个Jittor变量(jt.Var)是否完全相等。如果每一个元素都一致,函数则返回True,否则返回False。
这个函数通过逐元素的比较来判断两个变量是否相等。首先,使用 `a == b` 操作进行元素级的比较。然后,通过 `.all()` 操作来验证所有元素是否与 `b` 一致。最后, `.item()` 操作将结果转化为Python的bool值。
传入变量应该是Jittor的Var类型,a和b的尺寸应该是一样的。如果不一样,函数将抛出ValueError`。
参数:
- a (jt.Var) : 第一个Jittor变量,用于比较的输入
- b (jt.Var): 第二个Jittor变量,用于比较的输入
返回值:
bool类型,如果a和b所有元素相等,返回True,否则返回False
代码示例:
>>> a = jt.array([1, 2, 3])
>>> b = jt.array([1, 2, 3])
>>> print(all_equal(a, b))
True
>>> a = jt.array([1, 2, 3])
>>> b = jt.array([1, 2, 4])
>>> print(all_equal(a, b))
False
'''
return (a == b).all().item()
jt.all_equal = all_equal
def _to_float(x: jt.Var):
'''
将指定的jt.Var变量转化为'float32'类型。
参数:
- x: 需要转换的jt.Var变量。
示例代码:
>>> x = jt.Var([1])
>>> y = x._to_float()
>>> x,y
jt.Var([1], dtype=int32) jt.Var([1.], dtype=float32)
返回值:
转换类型后的jt.Var变量。
'''
if x.dtype != "float64": x = x.float()
return x
jt.Var._to_float = _to_float
[文档]
def index_select(x: jt.Var, dim:int, index: jt.Var):
'''
该方法返回一个新的张量,该张量通过使用索引在dim维度上对输入张量x进行索引。
返回的张量具有与原始张量(x)相同的维度数。dim维度的大小与索引的长度相同; 其他维度的大小与原始张量中的大小相同。
参数:
- x: jt.Var类型,输入的张量。
- dim: int类型,需要进行索引的维度。
- index: jt.Var类型,包含要索引的索引的1-D张量。
返回值:
jt.Var类型,新的通过索引获得的张量。
代码示例::
>>> x = jt.randn(3, 4)
>>> indices = torch.tensor([2, 1])
>>> y = jt.index_select(x, 0, indices)
>>> assert jt.all_equal(y, x[indices])
>>> y = jt.index_select(x, 1, indices)
>>> assert jt.all_equal(y, x[:, indices])
返回值:
jt.Var类型,新的通过索引获得的张量。
'''
return x.getitem(((slice(None),)*dim)+(index,))
jt.index_select = index_select
[文档]
def multinomial(weights: jt.Var, num_samples: int, replacement: bool=False):
'''
返回一个var,其中每一行包含从对应输入权重行的多项分布中抽取的 ``num_samples`` 个索引。
参数:
- weights(``Var``): 输入概率。(如:``weights = jt.float32([0, 10, 3, 0])`` )
- num_samples(``int``): 抽取的样本数量(如:``num_samples = 4``)。这个参数必须小于等于weights的个数。换句话说如果你在不放回的情况下设置num_samples为比weights个数还多,将会报错。
- replacement(``bool``): 是否放回地抽样,默认为 ``False``。如果设置为 ``True`` ,那么每一次抽样权重不变,可以多次抽到同一个。反之,每抽一次,权重会进行相应改变,不会抽到同一个。
代码示例:
>>> weights = jt.float32([0, 10, 3, 0])
... x = jt.multinomial(weights, 2)
... assert jt.all_equal(x, [1, 2]) or jt.all_equal(x, [2, 1])
... x = jt.multinomial(weights, 4, replacement=True)
... assert x.shape == (4, )
>>> weights = jt.float32([[0,0,2],[0,1,0], [0.5,0,0]])
... x = jt.multinomial(weights, 1)
... assert jt.all_equal(x, [[2],[1],[0]])
返回值:
返回一个var,其中每一行包含从对应输入权重行的多项分布中抽取的 ``num_samples`` 个索引。
'''
if replacement:
cum_probs = jt.cumsum(weights)[..., None, :]
cum_probs_l = cum_probs[..., :-1]
cum_probs_r = cum_probs[..., 1:]
shape = weights.shape[:-1] + (num_samples, 1)
rand = jt.rand(shape) * cum_probs[..., :1, -1:]
one_hot = jt.logical_and(cum_probs_l < rand, rand <= cum_probs_r)
index = one_hot.index(one_hot.ndim - 1) + 1
return (one_hot * index).sum(-1)
else:
# A-Res algorithm
# Pavlos S. Efraimidis and Paul G. Spirakis, 2006, Weighted random sampling with a reservoir
assert num_samples <= weights.shape[-1], "num_samples larger than the input"
# prevent rand generate 1, 1^inf = 1, with override other result
a = jt.rand(weights.shape).minimum(0.999999)
rand = a ** (1/weights)
_, indices = jt.topk(rand, num_samples)
return indices
[文档]
def histc(input, bins, min=0., max=0.):
'''
计算输入的N维数组的直方图。
元素被排序进入最小值和最大值之间等宽的箱中。若 min 和 max 都为0,则会取输入数组的最小值和最大值作为范围。返回的结果中,每个箱的值表示输入数组中值落在该 箱范围的元素数量。
参数:
- input(Var): 输入的数组
- bins(int): 直方图的分隔宽度数量
- min(float,optional): 箱的最小边界,默认值:0
- max(float,optional): 箱的最大边界,默认值:0
返回值:
计算得到的直方图(Var)
代码示例:
>>> inputs = jt.randn((40,40))
>>> jt.histc(inputs, bins=10)
jt.Var([ 1. 12. 40. 143. 323. 398. 386. 202. 72. 23.], dtype=float32)
'''
if min == 0 and max == 0:
min, max = input.min(), input.max()
assert min < max
bin_length = (max - min) / bins
histc = jt.floor((input[jt.logical_and(input >= min, input <= max)] - min) / bin_length).int().reshape(-1)
hist = jt.ones_like(histc).float().reindex_reduce("add", [bins,], ["@e0(i0)"], extras=[histc])
if hist.sum() != histc.shape[0]:
hist[-1] += 1
return hist
[文档]
def peek_s(x):
'''
返回一个Var的构成的字符串。
参数:
- x (``Var``): 输入的张量。
返回值:
``str``,返回一个递归组成的字符串。
代码示例:
>>> import jittor as jt
>>> a = [jt.ones((5,4))]
>>> jt.peek_s(a)
'[float32[5,4,], ]'
'''
if isinstance(x, jt.Var):
return x.peek()
if isinstance(x, (list, tuple)):
res = "["
for a in x:
res += peek_s(a)
res += ", "
res += "]"
return res
if isinstance(x, dict):
res = "{"
for a in x:
res += a
res += ":"
res += peek_s(x[a])
res += ", "
res += "}"
return res
if isinstance(x, str):
return x
return x.__class__.__name__
[文档]
def peek(x):
'''
输出一个Var的构成的字符串。
参数:
- x (``Var``): 输入的张量。
代码示例:
>>> import jittor as jt
>>> a = [jt.ones((5,4))]
>>> jt.peek(a)
[float32[5,4,], ]
'''
print(peek_s(x))
[文档]
class Finfo:
'''
浮点数类型相关参数。
由 ``jt.finfo`` 返回,包括浮点数的相关参数。
属性:
- min: 该浮点数类型的最小有限值,值类型取决于选取的浮点数类型
- max: 该浮点数类型的最大有限值,值类型取决于选取的浮点数类型
代码示例:
>>> jt.finfo('float32').min
-3.4028235e+38
>>> jt.finfo('bfloat16').min
-1e+38
'''
pass
bfloat16_finfo = Finfo()
bfloat16_finfo.min = -1e38
bfloat16_finfo.max = 1e38
[文档]
def finfo(dtype):
'''
根据指定的数据类型,返回其numpy浮点数类型的机器限制信息。numpy为每个浮点数提供了一个finfo对象,该对象提供了关于机器限制的信息,例如浮点数类型可以表示的最大和最小的正值、精度和“数字”的数量等,参见 ``https://numpy.org/doc/stable/reference/generated/numpy.finfo.html``
参数:
- dtype (str) : 指定的数据类型。例如:'float16'。
返回值:
返回对应指定数据类型的 numpy finfo 对象或者 'bfloat16_finfo'对象。
代码示例:
>>> from jittor import misc
>>> misc.finfo('float32')
finfo(resolution=1e-06, min=-3.4028235e+38, max=3.4028235e+38, dtype=float32)
'''
if dtype == "bfloat16":
return bfloat16_finfo
return np.finfo(str(dtype).split('.')[-1])
[文档]
def iinfo(dtype):
'''
此函数返回给定数据类型可表示的最大/最小整数的信息。此函数仅支持整数类型,如果dtype不是整数类型,会引发ValueError。
参数:
- dtype (str):期望获得其信息的数据类型的名称。如 `'int32'` 、 `'int16'` 等。
返回值:
返回一个iinfo对象,它有以下属性:min, max, dtype等。
代码示例:
>>> from jittor import misc
>>> misc.iinfo('int32')
iinfo(min=-2147483648, max=2147483647, dtype=int32)
'''
return np.iinfo(str(dtype).split('.')[-1])
# def index_select(input,dim,indices):
# return input[(None,)*dim+(indices,)]
# jt.Var.index_select = index_select
[文档]
def cuda(x):
'''
这是一个用于将Jittor的全局标记 `use_cuda` 设置为1,并返回输入值的函数。当 `use_cuda` 被设置为1时,这意味着Jittor将使用CUDA进行加速。
参数
- x : 输入值, 类型不限。
返回值:
x : 传入该函数的输入值。
代码示例:
>>> import jittor as jt
>>> jt.flags.use_cuda
0
>>> jt.cuda(5)
5
>>> jt.flags.use_cuda
1
'''
jt.flags.use_cuda = 1
return x
jt.Var.cuda = cuda
jt.Var.npu = cuda