框架模型层
运行过程与结果与NV相同
实现了一个使用tvm库进行矩阵乘法的程序。该程序在设备上执行矩阵乘法运算,并测量其性能。
- 包含必要的库和头文件,包括运行库和辅助函数
- 定义矩阵乘法的维度: 设置矩阵 (A) 的大小为 (320* 640),矩阵 (B) 的大小为 (640* 320)。
- 构建计算图:使用
te.placeholder
定义输入矩阵 (A) 和 (B)。使用te.compute
定义输出矩阵 (C) 的计算逻辑,利用te.sum
进行矩阵乘法。 - 创建调度:使用
te.create_schedule
创建调度,并为 GPU 设置线程和块的调度。使用s[C].split
和s[C].bind
将计算任务分配到不同的 GPU 线程和块。 - 构建和运行函数
build_and_run
:编译计算图为可执行的函数,并为输入矩阵分配随机数据。在设备上分配内存,创建 TVM 数组。计算 FLOPs,并在循环中执行矩阵乘法多次以计时。 - 计算性能指标:计算总运行时间和每秒浮点运算次数 (GFLOPS),并输出结果。
- 执行代码: 调用
build_and_run
函数在 GPU 上执行矩阵乘法,并打印计算图的简化模式。
代码:
import tvm
from tvm import te
import numpy as np
import time
# 定义矩阵乘法的大小
M = 320
N = 640
K = 320
# 定义矩阵乘法
A = te.placeholder((M, N), name='A')
B = te.placeholder((N, K), name='B')
k = te.reduce_axis((0, N), name='k')
C = te.compute((M, K), lambda i, j: te.sum(A[i, k] * B[k, j], axis=k), name='C')
# 创建调度
s = te.create_schedule(C.op)
# GPU 线程调度
block_x = te.thread_axis("blockIdx.x")
block_y = te.thread_axis("blockIdx.y")
thread_x = te.thread_axis("threadIdx.x")
thread_y = te.thread_axis("threadIdx.y")
# 为 GPU 添加块和线程的调度
bx, tx = s[C].split(C.op.axis[0], factor=32)
by, ty = s[C].split(C.op.axis[1], factor=32)
s[C].bind(bx, block_x)
s[C].bind(by, block_y)
s[C].bind(tx, thread_x)
s[C].bind(ty, thread_y)
# 定义函数
def build_and_run(target_device="rocm", num_repeats=300):
# 编译
target = tvm.target.Target(target_device)
f = tvm.build(s, [A, B, C], target=target, name='matmul')
# 创建输入数据
a_np = np.random.uniform(-1, 1, size=(M, N)).astype(np.float32)
b_np = np.random.uniform(-1, 1, size=(N, K)).astype(np.float32)
c_np = np.zeros((M, K), dtype=np.float32)
# 在设备上分配内存
dev = tvm.device(target_device, 0)
a_tvm = tvm.nd.array(a_np, dev)
b_tvm = tvm.nd.array(b_np, dev)
c_tvm = tvm.nd.array(c_np, dev)
# 计算 FLOPs(2 * M * N * K)
flops = 2 * M * N * K
# 运行并计时
start_time = time.time()
for i in range(num_repeats):
f(a_tvm, b_tvm, c_tvm)
dev.sync() # 保证所有计算都已完成
end_time = time.time()
# 计算总时间和 GFLOPS
total_time = end_time - start_time
gflops = (flops * num_repeats) / (total_time * 1e9)
# 输出结果
print(f"Execution on {target_device} completed in {total_time:.4f} seconds for {num_repeats} iterations.")
print(f"FLOPs: {flops} per matrix multiplication")
print(f"GFLOPS: {gflops:.2f} GFLOPS")
# 在 GPU 上执行
build_and_run(target_device="rocm")
结果:
Execution on rocm completed in 0.1786 seconds for 300 iterations.
FLOPs: 131072000 per matrix multiplication
GFLOPS: 220.18 GFLOPS
实现了一个使用 TVM 的Auto-scheduling 进行算子优化。
- 定义一个带有偏置加法的矩阵乘法。这里使用了 TVM 张量表达式语言中的标准操作。区别在于函数定义上方使用了
register_workload
装饰器。该函数应返回输入/输出张量列表。通过这些张量,auto-scheduler 可以得到整个计算图。 - 定义函数后,可以为 auto_scheduler 创建要搜索的任务。为这个矩阵乘法指定了特定的参数,如这里是两个大小为 1024x1024 的矩阵乘法。然后创建一个 N=L=M=1024 和 dtype="float32" 的搜索任务
num_measure_trials
表示搜索过程中可用的测试试验次数。用RecordToFile
将测试记录记录到文件matmul.json
中。测试记录可用于查询历史最佳、恢复搜索以及以后进行更多分析。- auto-scheduling 完成后,可将 schedule 降级来查看 IR。auto-scheduler 执行合适的优化,包括多级循环切分、布局转换、并行化、向量化、循环展开和算子融合。
代码:
import logging
import sys
import numpy as np
import tvm
from tvm import te
import tvm.testing
from tvm import autotvm
@auto_scheduler.register_workload # Note the auto_scheduler decorator
def matmul_add(N, L, M, dtype):
A = te.placeholder((N, L), name="A", dtype=dtype)
B = te.placeholder((L, M), name="B", dtype=dtype)
C = te.placeholder((N, M), name="C", dtype=dtype)
k = te.reduce_axis((0, L), name="k")
matmul = te.compute(
(N, M),
lambda i, j: te.sum(A[i, k] * B[k, j], axis=k),
name="matmul",
attrs={"layout_free_placeholders": [B]}, # enable automatic layout transform for tensor B
)
out = te.compute((N, M), lambda i, j: matmul[i, j] + C[i, j], name="out")
return [A, B, C, out]
target = tvm.target.Target("llvm")
N = L = M = 1024
task = tvm.auto_scheduler.SearchTask(func=matmul_add, args=(N, L, M, "float32"), target=target)
# 检查计算图
print("Computational DAG:")
print(task.compute_dag)
log_file = "matmul.json"
tune_option = auto_scheduler.TuningOptions(
num_measure_trials=10,
measure_callbacks=[auto_scheduler.RecordToFile(log_file)],
verbose=2,
)
# 运行 auto-tuning(搜索)
task.tune(tune_option)
# 应用最佳 schedule
sch, args = task.apply_best(log_file)
print("Lowered TIR:")
print(tvm.lower(sch, args, simple_mode=True))
结果:
Computational DAG:
A = PLACEHOLDER [1024, 1024]
B = PLACEHOLDER [1024, 1024]
matmul(i, j) += (A[i, k]*B[k, j])
C = PLACEHOLDER [1024, 1024]
out(i, j) = (matmul[i, j] + C[i, j])
Lowered TIR:
@main = primfn(A_1: handle, B_1: handle, C_1: handle, out_1: handle) -> ()
attr = {"from_legacy_te_schedule": True, "global_symbol": "main", "tir.noalias": True}
buffers = {A: Buffer(A_2: Pointer(float32), float32, [1048576], []),
B: Buffer(B_2: Pointer(float32), float32, [1048576], []),
C: Buffer(C_2: Pointer(float32), float32, [1048576], []),
out: Buffer(out_2: Pointer(float32), float32, [1048576], [])}
buffer_map = {A_1: A, B_1: B, C_1: C, out_1: out}
preflattened_buffer_map = {A_1: A_3: Buffer(A_2, float32, [1024, 1024], []), B_1: B_3: Buffer(B_2, float32, [1024, 1024], []), C_1: C_3: Buffer(C_2, float32, [1024, 1024], []), out_1: out_3: Buffer(out_2, float32, [1024, 1024], [])} {
allocate(auto_scheduler_layout_transform: Pointer(global float32), float32, [1048576]), storage_scope = global {
for (ax0.ax1.fused.ax2.fused: int32, 0, 128) "parallel" {
for (ax4: int32, 0, 256) {
for (ax6: int32, 0, 4) {
for (ax7: int32, 0, 8) {
auto_scheduler_layout_transform_1: Buffer(auto_scheduler_layout_transform, float32, [1048576], [])[((((ax0.ax1.fused.ax2.fused*8192) + (ax4*32)) + (ax6*8)) + ax7)] = B[((((ax4*4096) + (ax6*1024)) + (ax0.ax1.fused.ax2.fused*8)) + ax7)]
}
}
}
}
for (i.outer.outer.j.outer.outer.fused: int32, 0, 16384) "parallel" {
allocate(matmul: Pointer(global float32x8), float32x8, [4]), storage_scope = global;
for (i.outer.inner: int32, 0, 2) {
matmul_1: Buffer(matmul, float32x8, [4], [])[0] = broadcast(0f32, 8)
matmul_1[1] = broadcast(0f32, 8)
matmul_1[2] = broadcast(0f32, 8)
matmul_1[3] = broadcast(0f32, 8)
for (k.outer: int32, 0, 256) {
for (k.inner: int32, 0, 4) {
let cse_var_2: int32 = (((floormod(i.outer.outer.j.outer.outer.fused, 128)*8192) + (k.outer*32)) + (k.inner*8))
let cse_var_1: int32 = ((((floordiv(i.outer.outer.j.outer.outer.fused, 128)*8192) + (i.outer.inner*4096)) + (k.outer*4)) + k.inner)
{
matmul_1[0] = (matmul_1[0] + (broadcast(A[cse_var_1], 8)*auto_scheduler_layout_transform_1[ramp(cse_var_2, 1, 8)]))
matmul_1[1] = (matmul_1[1] + (broadcast(A[(cse_var_1 + 1024)], 8)*auto_scheduler_layout_transform_1[ramp(cse_var_2, 1, 8)]))
matmul_1[2] = (matmul_1[2] + (broadcast(A[(cse_var_1 + 2048)], 8)*auto_scheduler_layout_transform_1[ramp(cse_var_2, 1, 8)]))
matmul_1[3] = (matmul_1[3] + (broadcast(A[(cse_var_1 + 3072)], 8)*auto_scheduler_layout_transform_1[ramp(cse_var_2, 1, 8)]))
}
}
}
for (i.inner: int32, 0, 4) {
let cse_var_3: int32 = ((((floordiv(i.outer.outer.j.outer.outer.fused, 128)*8192) + (i.outer.inner*4096)) + (i.inner*1024)) + (floormod(i.outer.outer.j.outer.outer.fused, 128)*8))
out[ramp(cse_var_3, 1, 8)] = (matmul_1[i.inner] + C[ramp(cse_var_3, 1, 8)])
}
}
}
}
}
实现了在 Relay 中定义神经网络,并为 GPU 生成 runtime 库。
- 使用 Relay 框架定义了 ResNet18 神经网络模型,设定批量大小为 1,图像形状为 (3, 224, 224),输出类别数为 1000。
- 输出 ResNet18 模型的计算图结构,
show_meta_data=False
表示不显示元数据。 - 设置优化级别为 3(包括算子融合、预计算、布局变换等优化),并指定GPU作为目标设备,编译生成可在 GPU 上执行的库。
- 随机生成形状为
(1, 3, 224, 224)
的输入数据。创建一个执行模块,并将输入数据设置到模型中,然后运行模型并获取输出结果。输出结果中的前 10 个元素。 - 使用 TVM 的
utils.tempdir
创建临时目录,并将编译后的计算图、库和参数保存为文件,以便于后续部署时使用。 - 从保存的文件中加载编译模块,并使用相同的输入数据进行推理,获取输出结果。再次输出推理结果的前 10 个元素。
- 使用
tvm.testing.assert_allclose
检查重新加载的模块输出与最初输出是否一致,容差设置为 1e-5。
import numpy as np
from tvm import relay
from tvm.relay import testing
import tvm
from tvm import te
from tvm.contrib import graph_executor
import tvm.testing
batch_size = 1
num_class = 1000
image_shape = (3, 224, 224)
data_shape = (batch_size,) + image_shape
out_shape = (batch_size, num_class)
# 获取 ResNet 模型
mod, params = relay.testing.resnet.get_workload(
num_layers=18, batch_size=batch_size, image_shape=image_shape
)
# 为 AMD GPU (ROCm) 编译
opt_level = 3
target = tvm.target.rocm() # 改为 rocm 目标
with tvm.transform.PassContext(opt_level=opt_level):
lib = relay.build(mod, target, params=params)
# 创建图执行器,并在 AMD GPU 上运行该模块
# 创建随机输入
dev = tvm.rocm() # 改为使用 ROCm 设备
data = np.random.uniform(-1, 1, size=data_shape).astype("float32")
# 创建模块
module = graph_executor.GraphModule(lib["default"](dev))
# 设置输入和参数
module.set_input("data", data)
# 运行
module.run()
# 获取输出
out = module.get_output(0, tvm.nd.empty(out_shape)).numpy()
# 打印输出的前 10 个元素
print(out.flatten()[0:10])
结果:
[0.00089283 0.00103331 0.0009094 0.00102275 0.00108751 0.00106737
0.00106262 0.00095838 0.00110792 0.00113151]
# 创建随机输入
dev = tvm.rocm() # 改为 ROCm 设备
data = np.random.uniform(-1, 1, size=data_shape).astype("float32")
# 创建图执行器模块
module = graph_executor.GraphModule(lib["default"](dev))
# 设置输入数据和参数
module.set_input("data", data)
# 在 AMD GPU 上运行
module.run()
# 获取输出
out = module.get_output(0, tvm.nd.empty(out_shape)).numpy()
# 打印输出的前 10 个元素
print(out.flatten()[0:10])
结果:
[0.00089283 0.00103331 0.0009094 0.00102275 0.00108751 0.00106737
0.00106262 0.00095838 0.00110792 0.00113151]
# 保存和加载编译模块 分别将计算图、库和参数保存到不同文件
from tvm.contrib import utils
temp = utils.tempdir()
path_lib = temp.relpath("deploy_lib.tar")
lib.export_library(path_lib)
print(temp.listdir())
# 重新加载模块
loaded_lib = tvm.runtime.load_module(path_lib)
input_data = tvm.nd.array(data)
module = graph_executor.GraphModule(loaded_lib["default"](dev))
module.run(data=input_data)
out_deploy = module.get_output(0).numpy()
# 打印输出的前十个元素
print(out_deploy.flatten()[0:10])
# 检查来自部署模块的输出和原始输出是否一致
tvm.testing.assert_allclose(out_deploy, out, atol=1e-5)
结果:
['deploy_lib.tar']
[0.00089283 0.00103331 0.0009094 0.00102275 0.00108751 0.00106737
0.00106262 0.00095838 0.00110792 0.00113151]
实现了将 ONNX 模型编译到 TVM Runtime并使用 TVMC 运行来自编译模块的模型
- 从指定的 URL 下载图像,并保存为
imagenet_cat.png
。 - 使用 PIL 库将下载的图像大小调整为 224x224,以适应标准的图像输入要求(例如 ResNet)。
- 将图像数据从 HWC(Height-Width-Channel)格式转换为 NCHW(Channel-Height-Width)格式,这是 ONNX 模型的输入格式要求。
- 根据 ImageNet 的标准化方法,对图像进行归一化处理,减去均值
imagenet_mean
并除以标准差imagenet_stddev
。 - 将图像数据扩展一个维度,以符合神经网络模型所需的 batch 大小格式 (batch, channel, height, width)。
- 最终将预处理后的图像数据保存为
imagenet_cat.npz
,用于后续推理。 - 从指定的 URL 下载 ImageNet 的类别标签列表,并保存为
synset.txt
。 - 从保存的
predictions.npz
文件中加载输出张量,该文件应是神经网络推理后的结果。 - 使用 softmax 函数将模型的输出转化为概率分布。根据概率分数对输出进行排序,选出排名前 5 的类别,并打印它们的标签及对应的概率。
from tvm.contrib.download import download_testdata
from PIL import Image
import numpy as np
img_url = "https://s3.amazonaws.com/model-server/inputs/kitten.jpg"
img_path = download_testdata(img_url, "imagenet_cat.png", module="data")
# 重设大小为 224x224
resized_image = Image.open(img_path).resize((224, 224))
img_data = np.asarray(resized_image).astype("float32")
# ONNX 需要 NCHW 输入, 因此对数组进行转换
img_data = np.transpose(img_data, (2, 0, 1))
# 根据 ImageNet 进行标准化
imagenet_mean = np.array([0.485, 0.456, 0.406])
imagenet_stddev = np.array([0.229, 0.224, 0.225])
norm_img_data = np.zeros(img_data.shape).astype("float32")
for i in range(img_data.shape[0]):
norm_img_data[i, :, :] = (img_data[i, :, :] / 255 - imagenet_mean[i]) / imagenet_stddev[i]
# 添加 batch 维度
img_data = np.expand_dims(norm_img_data, axis=0)
# 保存为 .npz(输出 imagenet_cat.npz)
np.savez("imagenet_cat", data=img_data)
import os.path
import numpy as np
from scipy.special import softmax
from tvm.contrib.download import download_testdata
# 下载标签列表
labels_url = "https://s3.amazonaws.com/onnx-model-zoo/synset.txt"
labels_path = download_testdata(labels_url, "synset.txt", module="data")
with open(labels_path, "r") as f:
labels = [l.rstrip() for l in f]
output_file = "predictions.npz"
# 打开并读入输出张量
if os.path.exists(output_file):
with np.load(output_file) as data:
scores = softmax(data["output_0"])
scores = np.squeeze(scores)
ranks = np.argsort(scores)[::-1]
for rank in ranks[0:5]:
print("class='%s' with probability=%f" % (labels[rank], scores[rank]))
结果:
class='n02123045 tabby, tabby cat' with probability=0.621104
class='n02123159 tiger cat' with probability=0.356378
class='n02124075 Egyptian cat' with probability=0.019712
class='n02129604 tiger, Panthera tigris' with probability=0.001215
class='n04040759 radiator' with probability=0.000262
实现了使用 AutoTVM 在 TVM 中编译和优化 ONNX 模型。
-
使用
onnx.load()
加载 ONNX 模型。 -
下载一张图像并将其调整为 224x224 像素,这是 ResNet 等模型的标准输入大小。根据 ImageNet 的标准对图像进行归一化,并调整为 NCHW 格式。
-
使用 Relay 前端编译模型,并指定目标架构( GPU)。
-
构建模型并将其转换为图模块以便执行。
-
使用 TVM 的运行时运行模型以获取预测结果,并使用 softmax 处理结果以获得每个类别的概率。
-
使用
timeit
测量推理运行时间,并保存优化和未优化模型的结果。 -
使用 TVM 的 AutoTVM 中的
XGBTuner
启动调优过程。 -
设置调优选项并在从模型中提取的任务上运行调优。
-
在调优后,使用在调优过程中找到的最佳配置重新构建模型,并验证优化模型的预测结果。
-
打印优化模型和未优化模型的性能指标以进行比较。
import onnx from tvm.contrib.download import download_testdata from PIL import Image import numpy as np import tvm.relay as relay import tvm from tvm.contrib import graph_executor model_url = ( "https://github.com/onnx/models/raw/main/" "vision/classification/resnet/model/" "resnet50-v2-7.onnx" ) model_path = download_testdata(model_url, "resnet50-v2-7.onnx", module="onnx") onnx_model = onnx.load(model_path) img_url = "https://s3.amazonaws.com/model-server/inputs/kitten.jpg" img_path = download_testdata(img_url, "imagenet_cat.png", module="data") # 重设大小为 224x224 resized_image = Image.open(img_path).resize((224, 224)) img_data = np.asarray(resized_image).astype("float32") # 输入图像是 HWC 布局,而 ONNX 需要 CHW 输入,所以转换数组 img_data = np.transpose(img_data, (2, 0, 1)) # 根据 ImageNet 输入规范进行归一化 imagenet_mean = np.array([0.485, 0.456, 0.406]).reshape((3, 1, 1)) imagenet_stddev = np.array([0.229, 0.224, 0.225]).reshape((3, 1, 1)) norm_img_data = (img_data / 255 - imagenet_mean) / imagenet_stddev # 添加 batch 维度,期望 4 维输入:NCHW。 img_data = np.expand_dims(norm_img_data, axis=0) # 为 numpy 的 RNG 设置 seed,得到一致的结果 np.random.seed(0) target = "rocm" # 可用 Netron 工具检查输入名称 input_name = "data" shape_dict = {input_name: img_data.shape} mod, params = relay.frontend.from_onnx(onnx_model, shape_dict) with tvm.transform.PassContext(opt_level=3): lib = relay.build(mod, target=target, params=params) dev = tvm.device(str(target), 0) module = graph_executor.GraphModule(lib["default"](dev)) #在 TVM Runtime 执行 dtype = "float32" module.set_input(input_name, img_data) module.run() output_shape = (1, 1000) tvm_output = module.get_output(0, tvm.nd.empty(output_shape)).numpy() #收集基本性能数据 import timeit timing_number = 10 timing_repeat = 10 unoptimized = ( np.array(timeit.Timer(lambda: module.run()).repeat(repeat=timing_repeat, number=timing_number)) * 1000 / timing_number ) unoptimized = { "mean": np.mean(unoptimized), "median": np.median(unoptimized), "std": np.std(unoptimized), } print(unoptimized)
结果:
class='n02123045 tabby, tabby cat' with probability=0.621103 class='n02123159 tiger cat' with probability=0.356379 class='n02124075 Egyptian cat' with probability=0.019712 class='n02129604 tiger, Panthera tigris' with probability=0.001215 class='n04040759 radiator' with probability=0.000262
#调优模型 import tvm.auto_scheduler as auto_scheduler from tvm.autotvm.tuner import XGBTuner from tvm import autotvm logging.basicConfig(level=logging.DEBUG) number = 10 repeat = 1 min_repeat_ms = 100 # 对于 GPU 设置为一个合理值,通常不为 0 timeout = 10 # 秒 # 创建 TVM 运行器,针对 GPU 不需要 CPU 缓存刷新 runner = autotvm.LocalRunner( number=number, repeat=repeat, timeout=timeout, min_repeat_ms=min_repeat_ms, enable_cpu_cache_flush=False, # GPU 不需要清空 CPU 缓存 ) # 使用 XGBoost 算法来指导搜索。对于 GPU 推荐 3000-4000 次试验 tuning_option = { "tuner": "xgb", "trials": 4000, # 对于 GPU 调优,推荐更高的试验次数 "early_stopping": 800, # 设置一个较大的早停值 "measure_option": autotvm.measure_option( builder=autotvm.LocalBuilder(build_func="default"), runner=runner ), "tuning_records": "resnet-50-v2-autotuning-gpu.json", # 记录调优结果的文件 } # 设置目标为rocm,表示 GPU target = "rocm" # 从 onnx 模型中提取任务 tasks = autotvm.task.extract_from_program(mod["main"], target=target, params=params) # 按顺序调优提取的任务 for i, task in enumerate(tasks): prefix = "[Task %2d/%2d] " % (i + 1, len(tasks)) # 选择 XGBoost 调优器 tuner = "xgb" # 创建调优器 if tuner == "xgb": tuner_obj = XGBTuner(task, loss_type="reg") else: raise ValueError("Invalid tuner: " + tuner) # 开始调优 tuner_obj.tune( n_trial=min(tuning_option["trials"], len(task.config_space)), early_stopping=tuning_option["early_stopping"], measure_option=tuning_option["measure_option"], callbacks=[ autotvm.callback.progress_bar(tuning_option["trials"], prefix=prefix), autotvm.callback.log_to_file(tuning_option["tuning_records"]), ], )
结果:
[Task 25/25] Current/Best: 0.00/ 0.00 GFLOPS | Progress: (0/20) | 0.00 s [Task 25/25] Current/Best: 1.56/ 2.93 GFLOPS | Progress: (4/20) | 9.63 s [Task 25/25] Current/Best: 5.65/ 7.64 GFLOPS | Progress: (8/20) | 18.43 s [Task 25/25] Current/Best: 5.95/ 7.64 GFLOPS | Progress: (12/20) | 29.31 s [Task 25/25] Current/Best: 5.80/ 9.36 GFLOPS | Progress: (16/20) | 36.11 s [Task 25/25] Current/Best: 2.94/ 9.36 GFLOPS | Progress: (20/20) | 51.33 s
#使用调优数据编译优化模型,获取存储在 resnet-50-v2-autotuning.json(上述调优过程的输出文件)中的调优记录 with autotvm.apply_history_best(tuning_option["tuning_records"]): with tvm.transform.PassContext(opt_level=3, config={}): lib = relay.build(mod, target=target, params=params) dev = tvm.device(str(target), 0) module = graph_executor.GraphModule(lib["default"](dev)) #验证优化模型是否运行并产生相同的结果: dtype = "float32" module.set_input(input_name, img_data) module.run() output_shape = (1, 1000) tvm_output = module.get_output(0, tvm.nd.empty(output_shape)).numpy() scores = softmax(tvm_output) scores = np.squeeze(scores) ranks = np.argsort(scores)[::-1] for rank in ranks[0:5]: print("class='%s' with probability=%f" % (labels[rank], scores[rank]))
结果:
class='n02123045 tabby, tabby cat' with probability=0.621104 class='n02123159 tiger cat' with probability=0.356378 class='n02124075 Egyptian cat' with probability=0.019712 class='n02129604 tiger, Panthera tigris' with probability=0.001215 class='n04040759 radiator' with probability=0.000262
#比较调优和未调优的模型,收集与此优化模型相关的一些基本性能数据,并将其与未优化模型进行比较。 import timeit timing_number = 10 timing_repeat = 10 optimized = ( np.array(timeit.Timer(lambda: module.run()).repeat(repeat=timing_repeat, number=timing_number)) * 1000 / timing_number ) optimized = {"mean": np.mean(optimized), "median": np.median(optimized), "std": np.std(optimized)} print("optimized: %s" % (optimized)) print("unoptimized: %s" % (unoptimized))
结果:
optimized: {'mean': 407.31687583000166, 'median': 407.3377107500164, 'std': 1.692177042688564} unoptimized: {'mean': 495.13895513002353, 'median': 494.6680843500417, 'std': 1.3081147373726523}