机器学习(MachineLearning)(8)——模型评估与优化

机器学习(MachineLearning)(1)——机器学习概述
机器学习(MachineLearning)(2)——线性回归
机器学习(MachineLearning)(3)——决策树回归
机器学习(MachineLearning)(4)---------分类_逻辑回归
机器学习(MachineLearning)(5)——分类_决策树
机器学习(MachineLearning)(6)——分类_支持向量机
机器学习(MachineLearning)(7)——分类_朴素贝叶斯
机器学习(MachineLearning)(8)——模型评估与优化
机器学习(MachineLearning)(9)——聚类
机器学习(MachineLearning)(10)——TF-IDF

一、模型评估与优化

1. 模型评估

1)性能度量

① 错误率与精度

错误率和精度是分类问题中常用的性能度量指标,既适用于二分类任务,也适用于多分类任务.

  • 错误率(error rate):指分类错误的样本占样本总数的比例,即 ( 分类错误的数量 / 样本总数数量)

  • 精度(accuracy):指分类正确的样本占样本总数的比例,即 (分类正确的数量 / 样本总数数量)

    精度 = 1 − 错误率 精度 = 1 - 错误率 精度=1错误率

② 查准率、召回率与F1得分

错误率和精度虽然常用,但并不能满足所有的任务需求。例如,在一次疾病检测中,我们更关注以下两个问题:

  • 检测出感染的个体中有多少是真正病毒携带者?
  • 所有真正病毒携带者中,有多大比例被检测了出来?

类似的问题在很多分类场景下都会出现,“查准率”(precision)与“召回率”(recall)是更为适合的度量标准。对于二分类问题,可以将真实类别、预测类别组合为“真正例”(true positive)、“假正例”(false positive)、“真反例”(true negative)、“假反例”(false negative)四种情形,见下表:

在这里插入图片描述

  • 样例总数:TP + FP + TN + FN

  • 查准率: TP / (TP + FP),表示分的准不准

  • 召回率:TP / (TP + FN),表示分的全不全,又称为“查全率”

  • F1得分:
    f 1 = 2 ∗ 查准率 ∗ 召回率 查准率 + 召回率 f1 = \frac{2 * 查准率 * 召回率}{查准率 + 召回率} f1=查准率+召回率2查准率召回率

查准率和召回率是一对矛盾的度量。一般来说,查准率高时,召回率往往偏低;召回率高时,查准率往往偏低。例如,在病毒感染者检测中,若要提高查准率,只需要采取更严格的标准即可,这样会导致漏掉部分感染者,召回率就变低了;反之,放松检测标准,更多的人被检测为感染,召回率升高了,查准率又降低了. 通常只有在一些简单任务中,才能同时获得较高查准率和召回率。

查准率和召回率在不同应用中重要性也不同。例如,在商品推荐中,为了尽可能少打扰客户,更希望推荐的内容是用户感兴趣的,此时查准率更重要;而在逃犯信息检索系统中,希望让更少的逃犯漏网,此时召回率更重要。

③ 混淆矩阵

混淆矩阵也称误差矩阵,是表示精度评价的一种标准格式,用n行n列的矩阵形式来表示。每一行(数量之和)表示一个真实类别的样本,每一列(数量之和)表示一个预测类别的样本。

以下是一个预测结果准确的混淆矩阵:

A类别B类别C类别
A类别500
B类别060
C类别007

上述表格表示的含义为:A类别实际有5个样本,B类别实际有6个样本,C类别实际有7个样本;预测结果中,预测结果为A类别的为5个,预测结果为B类别的为6个,预测结果为C类别的为7个。

以下是一个预测结果不准确的混淆矩阵:

A类别B类别C类别
A类别311
B类别042
C类别007

上述表格表示的含义为:A类别实际有5个样本,B类别实际有6个样本,C类别实际有7个样本;预测结果中,A类别有3个样本预测准确,另外各有1个被预测成了B和C;B类别有4个预测准确,另外2个被预测成了C类别;C类别7个全部预测准确,但有1个本属于A类别、2个本属于B类别的被预测成了C类别。

根据混淆矩阵,查准率、召回率也可表示为:

查准率 = 主对角线上的值 / 该值所在列的和

召回率 = 主对角线上的值 / 该值所在行的和

④ 实验

利用sklearn提供的朴素贝叶斯分类器分类,并打印查准率、召回率、R2得分和混淆矩阵:

# 混淆矩阵示例
import numpy as np
import sklearn.model_selection as ms
import sklearn.metrics as sm
import sklearn.naive_bayes as nb# 输入,输出
x, y = [], []# 读取数据文件
with open("../data/multiple1.txt", "r") as f:for line in f.readlines():data = [float(substr) for substr in line.split(",")]x.append(data[:-1])  # 输入样本:取从第一列到导数第二列y.append(data[-1])  # 输出样本:取最后一列# 样本转数组
x = np.array(x)
y = np.array(y, dtype=int)# 划分训练集和测试集
train_x, test_x, train_y, test_y = ms.train_test_split(x, y, test_size=0.25, random_state=7)# 创建高斯朴素贝叶斯分类器对象
model = nb.GaussianNB()
model.fit(train_x, train_y)  # 使用划分的训练集来训练模型
pred_test_y = model.predict(test_x)  # 预测print("recall:", sm.recall_score(test_y,  # 真实值pred_test_y,  # 预测值average="macro"))  # 计算平均值,不考虑样本权重
print("precision:", sm.precision_score(test_y,  # 真实值pred_test_y,  # 预测值average="macro"))  # 计算平均值,不考虑样本权重
print("F1:", sm.f1_score(test_y, pred_test_y,average="macro"))# 计算并打印模型预测的混淆矩阵
print("\n Confusion Matrix:")
cm = sm.confusion_matrix(test_y, pred_test_y)
print(cm)

打印输出:

recall: 0.9910714285714286
precision: 0.9903846153846154
F1: 0.9905525846702318Confusion Matrix:
[[22  0  0  0][ 0 27  1  0][ 0  0 25  0][ 0  0  0 25]]

2)训练集与测试集

通常情况下,评估一个模型性能的好坏,将样本数据划分为两部分,一部分专门用于模型训练,这部分称为“训练集”,一部分用于对模型进行测试,这部分被称为“测试集”,训练集和测试集一般不存在重叠部分. 常用的训练集、测试集比例有:9:1, 8:2, 7:3等. 训练集和测试的划分,尽量保持均衡、随机,不能集中于某个或少量类别.

有些公共数据集在创建时,已经进行了划分. 有时候,我们需要自己对数据集进行划分,划分的方式是先打乱数据集,然后使用一种计算方法,将一部分数据划入训练集,一部分数据划入测试集.

在这里插入图片描述

3)交叉验证法

① 什么是交叉验证

在样本数量较少的情况下,如果将样本划分为训练集、测试集,可能导致单个集合样本数量更少,可以采取交叉验证法来训练和测试模型.

“交叉验证法”(cross validation)先将数据集D划分为k个大小相同(或相似)的、互不相交的子集,每个子集称为一个"折叠"(fold),每次训练,轮流使用其中的一个作为测试集、其它作为训练集. 这样,就相当于获得了k组训练集、测试集,最终的预测结果为k个测试结果的平均值.

在这里插入图片描述

② 如何实现交叉验证

sklearn中,提供了cross_val_score函数来实现交叉验证并返回评估指标值:

import sklearn.model_selection as msn = ms.cross_val_score(model, #模型train_x, train_y,# 样本输入、输出cv,  # 折叠数量scoring) # 指定返回的指标

以下是关于朴素贝叶斯模型的交叉验证实现:

# 交叉验证示例
import numpy as np
import sklearn.model_selection as ms
import sklearn.naive_bayes as nb
import matplotlib.pyplot as mpx, y = [], []  # 输入,输出# 读取数据文件
with open("../data/multiple1.txt", "r") as f:for line in f.readlines():data = [float(substr) for substr in line.split(",")]x.append(data[:-1])  # 输入样本:取从第一列到导数第二列y.append(data[-1])  # 输出样本:取最后一列train_x = np.array(x)
train_y = np.array(y, dtype=int)# 划分训练集和测试集
#train_x, test_x, train_y, test_y = ms.train_test_split(
#    x, y, test_size=0.25, random_state=7)# 创建高斯朴素贝叶斯分类器对象
model = nb.GaussianNB()
# 先做交叉验证,如果得分结果可以接受,再执行训练和预测
pws = ms.cross_val_score(model, x, y,cv=5,  # 折叠数量scoring='precision_weighted')  # 查准率
print("precision:", pws.mean())rws = ms.cross_val_score(model, x, y, cv=5,scoring='recall_weighted')  # 召回率
print("recall:", rws.mean())f1s = ms.cross_val_score(model, x, y, cv=5,scoring='f1_weighted')  # F1得分
print("f1:", f1s.mean())acc = ms.cross_val_score(model, x, y,cv=5, scoring='accuracy')  # 准确率
print("acc:", acc.mean())

执行结果:

precision: 0.996822033898305
recall: 0.9966101694915255
f1: 0.9966063988235516
acc: 0.9966101694915255

2. 模型优化

1)验证曲线与学习曲线

① 验证曲线

验证曲线是指根据不同的评估系数,来评估模型的优劣. 例如,构建随机森林,树的数量不同,模型预测准确度有何不同?以下是一个验证曲线的示例:

# 验证曲线示例
import numpy as np
import sklearn.preprocessing as sp
import sklearn.ensemble as se
import sklearn.model_selection as ms
import matplotlib.pyplot as mpdata = []
with open("../data/car.txt", "r") as f:for line in f.readlines():data.append(line.replace("\n", "").split(","))data = np.array(data).T  # 转置
encoders, train_x = [], []# 对样本数据进行标签编码
for row in range(len(data)):encoder = sp.LabelEncoder()  # 创建标签编码器encoders.append(encoder)if row < len(data) - 1:  # 不是最后一行,为样本特征lbl_code = encoder.fit_transform(data[row])  # 编码train_x.append(lbl_code)else:  # 最后一行,为样本输出train_y = encoder.fit_transform(data[row])train_x = np.array(train_x).T  # 转置回来,变为编码后的矩阵
# print(train_x)model = se.RandomForestClassifier(max_depth=8,  # 最大树高random_state=7)  # 随机种子
# 调用validation_curve,返回训练集、测试集得分矩阵
n_estimators = np.arange(50, 550, 50)  # 超参数值表
print("n_estimators.shape:", n_estimators.shape)
print("n_estimators:", n_estimators)# 通过不同参数,构建多棵决策树,验证其准确性
train_scores1, test_scores1 = ms.validation_curve(model,  # 模型train_x, train_y,'n_estimators',  # 模型参数名称n_estimators,  # 模型参数值cv=5)
train_mean = train_scores1.mean(axis=1)
print("train_mean:", train_mean)
test_mean = test_scores1.mean(axis=1)
print("test_mean:", test_mean)# 可视化
mp.figure('n_estimators', facecolor='lightgray')
mp.title('n_estimators', fontsize=20)
mp.xlabel('n_estimators', fontsize=14)
mp.ylabel('F1 Score', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.plot(n_estimators, test_mean, 'o-', c='blue', label='Testing')
mp.legend()
mp.show()

执行结果:

在这里插入图片描述

② 学习曲线

学习曲线是用来评估不同大小的训练集下模型的优劣程度,如果预测结果随着训练集样本的增加而变化不大,那么增加样本数量不会对模型产生明显优化作用. 以下是一个学习曲线的示例:

# 学习曲线示例
import numpy as np
import sklearn.preprocessing as sp
import sklearn.ensemble as se
import sklearn.model_selection as ms
import matplotlib.pyplot as mpdata = []
with open("../data/car.txt", "r") as f:for line in f.readlines():data.append(line.replace("\n", "").split(","))data = np.array(data).T  # 转置
encoders, train_x = [], []# 对样本数据进行标签编码
for row in range(len(data)):encoder = sp.LabelEncoder()  # 创建标签编码器encoders.append(encoder)if row < len(data) - 1:  # 不是最后一行,为样本特征lbl_code = encoder.fit_transform(data[row])  # 编码train_x.append(lbl_code)else:  # 最后一行,为样本输出train_y = encoder.fit_transform(data[row])train_x = np.array(train_x).T  # 转置回来,变为编码后的矩阵
print(train_x)# 获得学习曲线
model = se.RandomForestClassifier(max_depth=9,  # 最大树高n_estimators=200, # 评估系数random_state=7)  # 随机种子train_sizes = np.linspace(0.1, 1, 10)
train_sizes, train_scores, test_scores = ms.learning_curve(model,train_x, train_y,train_sizes=train_sizes,cv=5)#交叉验证折叠数量
train_means = train_scores.mean(axis=1)
test_means = test_scores.mean(axis=1)
for size, score in zip(train_sizes, train_means):print(size, '->', score)# 可视化
mp.figure('Learning Curve', facecolor='lightgray')
mp.title('Learning Curve', fontsize=20)
mp.xlabel('Train Size', fontsize=14)
mp.ylabel('F1 Score', fontsize=14)
mp.tick_params(labelsize=10)
mp.grid(linestyle=':')
mp.plot(train_sizes, train_means, 'o-', c='dodgerblue', label='Training')
mp.plot(train_sizes, test_means, 'o-', c='orangered', label='Testing')
mp.legend()
mp.show()

执行结果:

在这里插入图片描述

2)超参数优化

① 什么是超参数

超参数是在开始学习过程之前设置值的参数,而不是通过训练得到的参数数据。超参数的设置主要依赖于经验、实验或经过比较的优选值。以下是一些模型中常见的超参数:

  • 决策树模型树的最大深度;
  • 随机森林模型树的数量;
  • 交叉验证中折叠的额数量;
  • 训练集/测试集的比例等等.

超参数选择主要有随机搜索、网格搜索等方法。

② 网格搜索

网格搜索指将主要参数以及这些参数的主要取值,通过穷举法产生不同组合,计算并比较预测结果,来寻找这些参数的最优组合。

以下是利用网格搜索法,寻找SVM的最优超参数的示例:

# 网格搜索示例
import numpy as np
import sklearn.model_selection as ms
import sklearn.svm as svm
import sklearn.metrics as sm
import matplotlib.pyplot as mpx, y = [], []
with open("../data/multiple2.txt", "r") as f:for line in f.readlines():data = [float(substr) for substr in line.split(",")]x.append(data[:-1])  # 输入y.append(data[-1])  # 输出x = np.array(x)
y = np.array(y, dtype=int)# 通过网格搜索确定最优参数组合
# 定义参数字典
params = [{"kernel": ["linear"],"C": [1, 10, 100, 1000]},{"kernel": ["poly"],"C": [1],"degree": [2, 3]},{"kernel": ["rbf"],"C": [1, 10, 100, 1000],"gamma": [1, 0.1, 0.01, 0.001]}
]model = ms.GridSearchCV(svm.SVC(), params, cv=5)  # 创建网格搜索对象
model.fit(x, y)  # 训练print("best_score_:", model.best_score_)
print("best_params_:\n", model.best_params_)
#print("best_estimator_:\n", model.best_estimator_)l, r, h = x[:, 0].min() - 1, x[:, 0].max() + 1, 0.005
b, t, v = x[:, 1].min() - 1, x[:, 1].max() + 1, 0.005
grid_x = np.meshgrid(np.arange(l, r, h), np.arange(b, t, v))
flat_x = np.c_[grid_x[0].ravel(), grid_x[1].ravel()]
flat_y = model.predict(flat_x)
grid_y = flat_y.reshape(grid_x[0].shape)mp.figure("SVM RBF Classifier", facecolor="lightgray")
mp.title("SVM RBF Classifier", fontsize=14)
mp.xlabel("x", fontsize=14)
mp.ylabel("y", fontsize=14)
mp.tick_params(labelsize=10)
mp.pcolormesh(grid_x[0], grid_x[1], grid_y, cmap="gray")C0, C1 = (y == 0), (y == 1)
mp.scatter(x[C0][:, 0], x[C0][:, 1], c="orangered", s=80)
mp.scatter(x[C1][:, 0], x[C1][:, 1], c="limegreen", s=80)mp.show()

打印输出:

best_score_: 0.95
best_params_:{'C': 1, 'gamma': 1, 'kernel': 'rbf'}

执行结果可视化:

在这里插入图片描述

③ 随机搜索

随机搜索的思想与网格搜索比较相似,只是不再测试上界和下界之间的所有值,而是在搜索范围中随机选取样本点。它的理论依据是,如果样本点集足够大,那么通过随机采样也能大概率地找到全局最优值,或其近似值。随机搜索一般会比网格搜索要快一些,但是和网格搜索的快速版一样,它的结果也是没法保证的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/56750.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux_进程控制

一&#xff1a;进程创建 fork()函数创建新进程 #include <unistd.h> pid_t fork(void); 返回值&#xff1a;自进程中返回0&#xff0c;父进程返回子进程id&#xff0c;出错返回-1 进程调用fork&#xff0c;当控制转移到内核中的fork代码后&#xff0c;内核做&#xff1a;…

Spring Boot Web框架:智慧社区设计新思路

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…

cocos Creator + fairyGUI 快速入门

版本 Creator 3.8.x&#xff0c;FairyGUI 2022 大部分内容来自 https://en.fairygui.com/docs/sdk/creator 1.新建cocos项目&#xff0c;根目录运行 npm install --save fairygui-cc 引入 fairyGUI库 2.assets目录之外新建fairyGUI项目 3.fairyGUI中编辑UI 完成后发布到Creato…

uniapp,获取头部高度

头部自定义时候&#xff0c;设置获取安全区域&#xff0c;可以用 uni.getSystemInfoSync();接口。 <view class"statusBar" :style"{height:statusBarHeightpx}"> let SYSuni.getSystemInfoSync(); let statusBarHeightref(SYS.statusBarHeight) …

Python基础——类与对象

类与对象的理解&#xff1a; 在程序中我们将类看作是设计图纸&#xff0c;对象则是根据这个图纸生产的产品。面向对象编程就是使用对象编程&#xff0c;在类中我们定义成员属性和方法。 来看下面这个例子&#xff0c;创建student类&#xff0c;定义对象并对属性赋值。 class S…

解决linux服务器磁盘占满问题(详细,有效,100%解决)

应用场景&#xff1a; 在我们的日常开发中&#xff0c;我们的服务器总是在不知不觉中磁盘莫名奇妙少了很多空间&#xff0c;或者被占满了&#xff0c;如果这时候要想要存储什么文件&#xff0c;突然发现空间不够了。但我们通常也不知道那些文件占用的空间大&#xff0c;这时候…

Vue是一套构建用户界面的渐进式框架,常用于构建单页面应用

学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……&#xff09; 2、学会Oracle数据库入门到入土用法(创作中……&#xff09; 3、手把手教你开发炫酷的vbs脚本制作(完善中……&#xff09; 4、牛逼哄哄的 IDEA编程利器技巧(编写中……&#xff09; 5、面经吐血整理的 面试技…

红黑树的理解与实现(详解)

相关的数据结构&#xff1a; 搜索二叉树-CSDN博客 AVL树的创建与检测-CSDN博客 个人主页&#xff1a;敲上瘾-CSDN博客 个人专栏&#xff1a;游戏、数据结构、c语言基础、c学习、算法 目录 一、红黑树规则&#xff1a; 二、红黑树的插入 1.变色 2.单旋变色 3.双旋变色 三、…

WebGoat SQL Injection (intro) 源码分析

首先了解 java 中 mysql 的连接&#xff1a;java连接Mysql WebGoat SQL Injection (intro) 10 根据提示&#xff1a;下面两个输入框只有一个受到 sql 注入攻击。题目要求是检索到所有数据 发现请求路径为 SqlInjection/assignment5b 定位到所在文件如下&#xff0c;根据代码…

【uniapp】使用Promise封装request

目录 1、创建config目录 2、创建settings.js 3、创建目录utils 4、创建request.js 5、创建api目录 6、创建apis.js文件 7、业务系统调用 7.1 业务系统banner 7.2 业务系统荣誉页面&#xff08;传参&#xff09; 前言&#xff1a;使用Promise封装request 1、创建config…

UNIX网络编程-传输层

概述 传输层主要包括&#xff1a;TCP、UDP、SCTP&#xff08;流控制传输协议&#xff09;&#xff01; 绝大多数客户端/服务器网络应用都使用TCP/UDP。SCTP是一个较新的协议&#xff0c;最初设计用于跨因特网传输电话信令。 这些传输协议都转而使用网络协议IP&#xff1a;或是…

2023年华为杯数学建模竞赛题F论文和代码

强对流降水临近预报建模与优化 对问题一&#xff0c;为了实现基于前一小时&#xff08;10帧&#xff09;的实测雷达观测量&#xff08;ZH、ZDR、KDP&#xff09;&#xff0c;对后续一小时&#xff08;10帧&#xff09;的ZH进行预报&#xff0c;本文首先建立了线性拟合与RMSE双驱…

matlab相位图

% 清空工作空间和命令窗口 clear; clc; % 模拟生成时间t&#xff0c;位移y(t)和角位移theta(t) t linspace(0, 100, 1000); % 时间从0到100&#xff0c;包含1000个点 y 1e-5 * sin(2 * pi * 0.1 * t) .* exp(-0.01 * t); % 位移y(t) 振荡衰减 theta 1e-6 * cos(2 * pi * …

Qt第十三天:网络编程:TCP和UDP的使用

我发现了有些人喜欢静静看博客不聊天呐&#xff0c; 但是ta会点赞。 这样的人呢帅气低调有内涵&#xff0c; 美丽大方很优雅。 说的就是你&#xff0c; 不用再怀疑哦 ❤️TCP&#xff1a; 一、创建项目&#xff0c;命名为Server&#xff0c;继承QWidget 二、添加Qt设计师…

如何进行数学家式的学习思考?

如何进行数学家式的学习思考&#xff1f; 学生阶段的数学学习是非常重要的&#xff0c;对这一点很少有人质疑。一提起数学学习&#xff0c;一些学生、家长甚至一些教师认为&#xff0c;学生的数学学习往往侧重于掌握基本概念、公式和解题技巧&#xff0c;通过做题来巩固知识和提…

【飞腾加固服务器】全国产化解决方案:飞腾FT2000+/64核,赋能关键任务保驾护航

在信息安全和自主可控的时代背景下&#xff0c;国产化设备的需求与日俱增&#xff0c;尤其是在国防、航空航天、能源和其他关键行业。高可靠性和极端环境设计的国产加固服务器&#xff0c;搭载强大的飞腾FT2000/64核处理器&#xff0c;全面满足国产自主可控的严苛要求。 性能强…

【Linux探索学习】第六弹——Linux的工具(一):Ubuntu系统下的软件包管理器

前言&#xff1a; 在Ubuntu系统中&#xff0c;Linux工具为用户提供了强大的命令行操作能力。这些工具不仅使日常任务的自动化成为可能&#xff0c;还大幅提升了生产力。本文将重点介绍一些常用的Linux工具&#xff1a;软件包管理器 注意&#xff1a;本文是所讲解的内容是在Ubun…

【C++基础篇】——逐步了解C++

【C基础篇】——逐步了解C 文章目录 【C基础篇】——逐步了解C前言一、C的第一个程序二、命名空间1.namespace的价值2.namespace的定义3.命名空间的使用 三、C的输入&输出四、缺省参数五、函数重载六、引用1.引用的概念和定义&#xff1a;2.引用的特性3.引用的使用4.const引…

使用LangGraph构建多Agent系统架构!

0 前言 Agent是一个使用大语言模型决定应用程序控制流的系统。随着这些系统的开发&#xff0c;它们随时间推移变得复杂&#xff0c;使管理和扩展更困难。如你可能会遇到&#xff1a; Agent拥有太多的工具可供使用&#xff0c;对接下来应该调用哪个工具做出糟糕决策上下文过于…

51单片机的超声波视力保护仪【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块温度传感器光照传感器超声波传感器按键、LED、蜂鸣器等模块构成。适用于视力保护仪、坐姿矫正器、超声波防近视等相似项目。 可实现功能: 1、LCD1602显示温度、光照、距离和学习时间 2、超声波传感器采集头部与探…