核心思路
5 套题结构一致:给你一份 xlsx 数据 → 按题面在答题卷指定单元格填写答案。不需要写公式,但需要会用 Excel 分析数据得出结论。
五套题数据对比
| 题号 | 数据集 | 列名 | 分析维度 |
| 3.1.1 | 智能音箱 1001行 | 用户ID, 时间戳, 功能调用类型, 响应时间 | 功能频率 + 响应时间 |
| 3.1.2 | 智能照明 1001行 | 时间戳, 用户ID, 光线亮度, 色温, 场景, 响应时间 | 时段均值 + 场景频率 + 响应时间 |
| 3.1.3 | 健康手环 两Sheet | Sheet1: 用户/日期/步数 Sheet2: 用户/时间戳/功能/延迟 | 时段步数 + 功能偏好 + 延迟 |
| 3.1.4 | 健康监测 169行 | 时间戳, 收缩压, 舒张压, 血糖, 体脂, 响应时间 | 时段趋势 + 指标偏好 + 响应时间 |
| 3.1.5 | 家居环境 1001行 | 时间戳, 温度, 湿度, 光照, 响应时间, 能源消耗 | 时段均值 + 响应时间 + 能源 |
必考操作 → Excel 操作路径
| 功能 | 操作路径 / 公式 | 注意事项 |
数据透视表 核心 |
选中数据区域 → 插入 → 数据透视表 → 拖字段到行/列/值区域 |
值字段默认"计数",需要求均值时要改为"平均值" |
按时段分类 必考 |
透视表中拖"时间戳"到行区域 → 右键"组合" → 选择"小时"分组 或用公式:=HOUR(B2)提取小时后分类 |
时间列必须是日期格式,文本时间需要先转换 |
AVERAGEIF 常用 |
=AVERAGEIF(条件列, 条件, 求均值列) |
条件用英文引号,如 "播放音乐" |
COUNTIF 常用 |
=COUNTIF(范围, 条件) |
统计频次,排序后可判断"最常使用/较少使用" |
AVERAGE 基础 |
=AVERAGE(范围) |
计算平均响应时间、平均温度等 |
排序 辅助 |
选中数据 → 数据 → 排序 → 选择列和升/降序 |
先全选再排序,避免只排一列导致数据错位 |
条件格式 可能考 |
开始 → 条件格式 → 色阶/数据条/突出显示 |
用于可视化高/低值 |
答题模板(5 套通用)
| 答题区域 | 分析思路 | 方法 |
用户使用习惯 (频率/偏好) | 统计各功能/场景出现次数,排序 | COUNTIF 或透视表计数 |
时段均值 (温度/步数/亮度等) | 按时间段分组求平均 | 透视表组合 + 平均值 |
| 响应时间分析 | 求整体平均;找最长/最短 | AVERAGE / MAX / MIN |
| 优化方向 x3 | 从数据中找问题 → 写对策 | 见下方模板 |
优化方向万能模板
1. 响应时间优化:XX 功能响应时间过长 → 优化算法 / 增加缓存 / 负载均衡
2. 用户活跃度提升:XX 功能使用率低 → 优化交互设计 / 增加推荐引导
3. 数据质量改善:XX 指标波动大 → 增加采集频率 / 校准传感器
踩坑点
• 不要修改答题卷格式,只在指定单元格填写答案
• 答题中出现的具体数值(如"平均响应时间 2.3 秒")要和 Excel 算出来的对得上
• 3.1.3 有两个 Sheet,注意看题目要求用哪个
• 3.1.4 收缩压/舒张压属血压功能,血糖属血糖功能,体脂属体脂功能 — 分类别统计
• 时间分组时注意时段划分(如 06:00-12:00 vs 6:00-12:00),严格按题目要求
- 打开 .docx 题目文件,通读题目要求,圈出需要填写的具体项
- 打开对应 .xlsx 数据文件,快速浏览表头和数据量
- 打开答题卷(同一个 xlsx 或单独文件),找到需要填答案的单元格位置
- 对时间列做分组:插入数据透视表 → 拖时间戳到行 → 右键组合 → 按小时/时段
- 统计频率:透视表中拖功能/场景列到行区域,拖同一列到值区域(计数)
- 求均值:值字段改为"平均值",得出各时段/各功能的平均数值
- 分析响应时间:用 AVERAGE / MAX / MIN 分别算整体平均、最长、最短
- 将计算结果填入答题卷对应单元格(文字+数值)
- 写 3 条优化方向:根据数据中"最长/最短/最高/最低"的指标写问题和对策
- 检查:数值是否合理、单位是否正确、是否填在正确位置
标准流程四步走
加载模型 → 预处理图片 → 执行推理 → 后处理输出结果
五套题对比
| 题号 | 模型 | 图片 | 预处理 | 任务 | 难度 |
| 3.2.1 |
resnet.onnx ~100MB |
img_test.jpg RGB 彩色 |
resize 256 → center crop 224 → 归一化 mean/std ImageNet |
Top5 分类 |
中等 |
| 3.2.2 |
mnist.onnx ~26KB |
img_test.png 灰度 |
resize 28x28 → 灰度 expand_dims x2 |
手写数字识别 |
简单 |
| 3.2.3 |
emotion-ferplus.onnx ~33MB |
img_test.png 灰度 |
resize 64x64 → 灰度 expand_dims x2 |
情感分类 |
简单 |
| 3.2.4 |
flower-detection.onnx ~100MB |
flower_test.png RGB 彩色 |
同 3.2.1 的预处理函数 |
花卉分类 + 百分比 |
中等 |
| 3.2.5 |
version-RFB-320.onnx ~1.2MB |
imgs/ 目录多图 RGB |
resize 320x240 → 减均值/128 transpose + expand_dims |
人脸检测 + 画框 |
较难 |
关键认知:考试题分两种形式
• 填空版(.ipynb 中用 ______ 标记):需要补全代码
• 完成版:有正确答案可以参考,但也有故意留的错误(如 3.2.2 的拼写错误)
• 考试时拿到的一定是填空版,需要根据题目提示填入正确代码
分值分布 模型加载 2 + 加载图片 2 + 预处理 2 + 推理 2 + softmax 2 + Top5 3 = 13 填空分
# ===== 3.2.1 ResNet 图片分类 — 完整代码模板 =====
import onnxruntime as ort
import numpy as np
import scipy.special
from PIL import Image
# 预处理函数(题目已提供,不用填)
def preprocess_image(image, resize_size=256, crop_size=224,
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]):
image = image.resize((resize_size, resize_size), Image.BILINEAR)
w, h = image.size
left = (w - crop_size) / 2
top = (h - crop_size) / 2
image = image.crop((left, top, left + crop_size, top + crop_size))
image = np.array(image).astype(np.float32)
image = image / 255.0
image = (image - mean) / std
image = np.transpose(image, (2, 0, 1))
image = image.reshape((1,) + image.shape)
return image
# ① 加载模型 (2分)
session = ort.InferenceSession('resnet.onnx')
# 加载标签
with open('labels.txt') as f:
labels = [line.strip() for line in f.readlines()]
input_name = session.get_inputs()[0].name
output_name = session.get_outputs()[0].name
# ② 加载图片 (2分)
image = Image.open('img_test.jpg').convert('RGB')
# ③ 预处理图片 (2分)
processed_image = preprocess_image(image)
processed_image = processed_image.astype(np.float32)
# ④ 执行推理 (2分)
output = session.run([output_name], {input_name: processed_image})[0]
# ⑤ softmax (2分)
probabilities = scipy.special.softmax(output, axis=-1)
# ⑥ 取 Top5 (3分)
top5_idx = np.argsort(probabilities[0])[-5:][::-1]
top5_prob = probabilities[0][top5_idx]
print("Top 5 predicted classes:")
for i in range(5):
print(f"{i+1}: {labels[top5_idx[i]]} - Probability: {top5_prob[i]}")
踩坑点
• session.run() 返回的是列表,要加 [0] 取第一个输出
• Top5 取法:np.argsort()[-5:][::-1] — 先取最后5个(最大),再反转成降序
• top5_prob 不能写成 probabilities[top5_idx],要加 [0] 因为 probabilities 是三维的
分值分布 加载模型 2 + 加载图片 2 + resize 2 + np.array 2 + expand_dims x2 各 2 + get_inputs 2 + run 2 + argmax 2 = 18 填空分
# ===== 3.2.2 MNIST 手写数字 — 完整代码模板 =====
import onnxruntime
import numpy as np
from PIL import Image
# ① 加载模型 (2分)
ort_session = onnxruntime.InferenceSession('mnist.onnx')
# ② 加载图片 + 转灰度 (2分)
image = Image.open("img_test.png").convert('L')
# ③ resize (2分)
image = image.resize((28, 28))
# ④ 转 numpy (2分)
image_array = np.array(image, dtype=np.float32)
# ⑤ 添加 batch 维度 (2分)
image_array = np.expand_dims(image_array, axis=0)
# ⑥ 添加 channel 维度 (2分)
image_array = np.expand_dims(image_array, axis=0)
# ⑦ 获取输入名 (2分)
ort_inputs = {ort_session.get_inputs()[0].name: image_array}
# ⑧ 执行推理 (2分)
ort_outs = ort_session.run(None, ort_inputs)
# ⑨ 取最大概率的类别 (2分)
predicted_class = np.argmax(ort_outs)
print(f"Predicted class: {predicted_class}")
踩坑点(本题有大量拼写陷阱!)
• np.expend_dims 是错的!正确是 np.expand_dims
• ort_seesion 是错的!正确是 ort_session
• ort_session.get_input() 是错的!正确是 get_inputs()(有 s)
• expand_dims 两次:第一次 axis=0 加 batch 维,第二次 axis=0 加 channel 维(因为已经是 3D 了)
• 最终形状应该是 (1, 1, 28, 28)
分值分布 情感映射表 3 + 加载模型 3 + 预处理调用 3 + 推理 3 + argmax 3 + 查表 3 = 18 填空分
# ===== 3.2.3 情感识别 — 完整代码模板 =====
import numpy as np
from PIL import Image
import onnxruntime as ort
# 预处理函数(题目已提供)
def preprocess(image_path):
input_shape = (1, 1, 64, 64)
img = Image.open(image_path).convert('L')
img = img.resize((64, 64), Image.ANTIALIAS)
img_data = np.array(img, dtype=np.float32)
img_data = np.expand_dims(img_data, axis=0)
img_data = np.expand_dims(img_data, axis=1)
assert img_data.shape == input_shape
return img_data
# ① 情感映射表 (3分) — 8 类情感
emotion_table = {0: 'Neutral', 1: 'Happy', 2: 'Sad', 3: 'Surprise',
4: 'Fear', 5: 'Disgust', 6: 'Anger', 7: 'Contempt'}
# ② 加载模型 (3分)
ort_session = ort.InferenceSession('emotion-ferplus.onnx')
# ③ 预处理图片 (3分)
input_data = preprocess('img_test.png')
ort_inputs = {ort_session.get_inputs()[0].name: input_data}
# ④ 执行推理 (3分)
ort_outs = ort_session.run(None, ort_inputs)
# ⑤ 取预测类别 (3分)
predicted_label = np.argmax(ort_outs[0])
# ⑥ 查情感名 (3分)
predicted_emotion = emotion_table[predicted_label]
print(f"Predicted emotion: {predicted_emotion}")
踩坑点
• 情感映射表要自己写全 8 类(0-7),别漏
• preprocess() 函数已提供,直接调用传图片路径即可
• ort_outs[0] 取第一个输出再 argmax,别对整个列表 argmax
分值分布 加载模型 2 + 加载标签 2 + 加载图片 2 + 预处理 2 + 推理 2 + softmax 2 + 取idx/prob/label 各算 = 共 20 分
# ===== 3.2.4 花卉分类 — 完整代码模板 =====
import onnxruntime as ort
import numpy as np
import scipy.special
from PIL import Image
# 预处理函数(同 3.2.1)
def preprocess_image(image, resize_size=256, crop_size=224,
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]):
image = image.resize((resize_size, resize_size), Image.BILINEAR)
w, h = image.size
left = (w - crop_size) / 2
top = (h - crop_size) / 2
image = image.crop((left, top, left + crop_size, top + crop_size))
image = np.array(image).astype(np.float32)
image = image / 255.0
image = (image - mean) / std
image = np.transpose(image, (2, 0, 1))
image = image.reshape((1,) + image.shape)
return image
# ① 加载模型 (2分)
session = ort.InferenceSession('flower-detection.onnx')
# ② 加载标签 (2分)
with open('labels.txt') as f:
labels = [line.strip() for line in f.readlines()]
input_name = session.get_inputs()[0].name
output_name = session.get_outputs()[0].name
# ③ 加载图片 (2分)
image = Image.open('flower_test.png').convert('RGB')
# ④ 预处理 (2分)
processed_image = preprocess_image(image)
processed_image = processed_image.astype(np.float32)
# ⑤ 推理 (2分)
output = session.run([output_name], {input_name: processed_image})[0]
# ⑥ softmax (2分)
accuracy = scipy.special.softmax(output, axis=-1)
# ⑦ 取预测索引
predicted_idx = np.argmax(accuracy)
# ⑧ 转百分比
prob_percentage = accuracy[0][predicted_idx] * 100
# ⑨ 取标签名
predicted_label = labels[predicted_idx]
print(f"Predicted class: {predicted_label}, Accuracy: {prob_percentage:.2f}%")
踩坑点
• 标签文件路径要填对:'labels.txt' 不是空字符串
• accuracy[0][predicted_idx] — 注意 accuracy 是三维的 (1, 1, N),要先 [0]
• 乘 100 转百分比,.2f 保留两位小数
• 和 3.2.1 的区别:只要 Top1,不是 Top5
分值分布 标签加载 2 + 加载模型 2 + 获取输入名 2 + 创建目录 2 + 读图 2 + resize 2 + 均值 2 + expand_dims 1 + run 2 = 17 填空分
# ===== 3.2.5 人脸检测 — 填空关键行 =====
# 已有导入和 predict 函数(不用填)
# ① 加载标签 (2分)
class_names = [name.strip() for name in open('voc-model-labels.txt').readlines()]
# ② 加载模型 (2分)
ort_session = ort.InferenceSession('version-RFB-320.onnx')
# ③ 获取输入名 (2分)
input_name = ort_session.get_inputs()[0].name
result_path = "./detect_imgs_results_onnx"
threshold = 0.7
path = "imgs"
sum = 0
# ④ 创建目录 (2分)
if not os.path.exists(result_path):
os.makedirs(result_path)
listdir = os.listdir(path)
for file_path in listdir:
img_path = os.path.join(path, file_path)
# ⑤ 读取图片 (2分)
orig_image = cv2.imread(img_path)
image = cv2.cvtColor(orig_image, cv2.COLOR_BGR2RGB)
# ⑥ resize (2分)
image = cv2.resize(image, (320, 240))
# ⑦ 均值 (2分)
image_mean = np.array([127, 127, 127])
image = (image - image_mean) / 128
image = np.transpose(image, [2, 0, 1])
# ⑧ expand_dims (1分)
image = np.expand_dims(image, axis=0)
image = image.astype(np.float32)
time_time = time.time()
# ⑨ 推理 (2分)
confidences, boxes = ort_session.run(None, {input_name: image})
print("cost time:{}".format(time.time() - time_time))
boxes, labels, probs = predict(orig_image.shape[1], orig_image.shape[0],
confidences, boxes, threshold)
for i in range(boxes.shape[0]):
box = boxes[i, :]
label = f"{class_names[labels[i]]}: {probs[i]:.2f}"
cv2.rectangle(orig_image, (box[0], box[1]), (box[2], box[3]),
(255, 255, 0), 4)
cv2.imwrite(os.path.join(result_path, file_path), orig_image)
sum += boxes.shape[0]
print("sum:{}".format(sum))
踩坑点
• 标签解析:name.strip() 不是 line.strip(),for 循环变量是 name
• 创建目录用 os.makedirs 不是 os.mkdir(虽然单层目录都可以)
• cv2.imread() 返回 BGR,要 cv2.cvtColor 转 RGB
• resize 参数是 (宽, 高) 即 (320, 240),不是 (240, 320)
• np.array([127,127,127]) — 别忘了 np.array
• 这题用 ort_session 不是 session,变量名要和前面一致
• 结果图片保存在 detect_imgs_results_onnx/ 目录
高频填空答案一览
| 填空位置 | 答案 | 出现频率 |
| 加载 ONNX 模型 | ort.InferenceSession('模型名.onnx') | 5/5 |
| 打开图片 | Image.open('图片名').convert('RGB') 或 'L' | 5/5 |
| 执行推理 | session.run(None, {input_name: data}) | 5/5 |
| 获取输入名 | session.get_inputs()[0].name | 4/5 |
| 取预测类别 | np.argmax(output) | 3/5 |
| 添加维度 | np.expand_dims(arr, axis=0) | 4/5 |
| softmax | scipy.special.softmax(output, axis=-1) | 2/5 |
| 读标签文件 | with open('labels.txt') as f: | 2/5 |
| 创建目录 | os.makedirs(path) | 1/5 |
3.2 通用操作步骤
- 打开 Jupyter Notebook,确认素材文件(.onnx 模型 + 图片 + labels.txt)都在同目录
- 先运行已有的 import 单元格,确认库都能正常导入
- 如果预处理函数已提供,先运行它(不需要修改)
- 依次填写:加载模型 → 加载标签/映射表 → 加载图片 → 调预处理 → 推理 → 后处理
- 注意检查变量名拼写,特别是
ort_session vs session
- 运行整个 Notebook,看输出是否正常(有预测结果,无报错)
- 保存 Notebook
全局踩坑点
• Image.ANTIALIAS 在新版 Pillow 中已弃用,新版用 Image.Resampling.LANCZOS,但考试环境可能是旧版
• ort.run(None, inputs) 第一个参数 None 表示返回所有输出
• 图片颜色模式:彩色用 .convert('RGB'),灰度用 .convert('L'),看题目要求
• numpy 数组形状一定要和模型输入匹配,不确定时用 print(arr.shape) 检查
• np.argmax() 返回的是索引号,不是概率值
拿满 20 分的检查清单
✓ 模型文件名拼写正确
✓ 图片文件名拼写正确
✓ 变量名前后一致
✓ 输出结果合理(概率在 0-1 之间,类别名不是乱码)
✓ Notebook 已保存
3.1 Excel 一句话口诀
透视表拉频率,AVERAGE 算均值,三大优化写上去
优化方向万能三条:①响应慢的加速 ②用得少的推广 ③数据波动的改善
3.2 ONNX 一句话口诀
InferenceSession 加模型,Image.open 读图片,run 做推理,argmax 取结果
检查三件事:文件名对不对、变量名一不一致、shape 匹不匹配
易错 API 速查
| 易错写法 | 正确写法 |
np.expend_dims | np.expand_dims |
ort_seesion | ort_session |
get_input() | get_inputs() |
Image.open().convert('gray') | .convert('L') |
cv2.resize(img, (高, 宽)) | cv2.resize(img, (宽, 高)) |
onnxruntime.InferenceSession | ort.InferenceSession(别名也可) |