神经网络拟合曲线及讨论

问题说明

神经网络能否拟合x^2 + y^2 = 100在第一象限的曲线?

设计思路

第一象限的曲线方程如下所示:
y=100−x2y = \sqrt{100-x^2} y=100−x2​
在[0, 10]中等距生成1000个点,划分训练集、开发集和测试集,构建神经网络训练。神经网络架构图如下,采用两层神经网络进行拟合,根据情况调节神经网络的深度以及隐藏层神经元个数。

代码实现

import randomimport matplotlib.pyplot as plt
import numpy as np
import torch
from torch import nn
from torch.utils.data import Dataset, DataLoader
import torch.nn.functional as Fdef setup_seed(seed):# 固定所有的随机数种子,使结果能够复现torch.manual_seed(seed)torch.cuda.manual_seed_all(seed)np.random.seed(seed)random.seed(seed)torch.backends.cudnn.deterministic = Truedef generate_data(num=1000):x = np.linspace(0, 10, num)y = [np.sqrt(100 - i ** 2) for i in x]return list(zip(x, y))def distribute_dataset(data, rate):total_num = len(data)train_pos = int(total_num * rate[0])dev_pos = int(total_num * (rate[0] + rate[1]))train_data = data[: train_pos]dev_data = data[train_pos: dev_pos]test_data = data[dev_pos:]return train_data, dev_data, test_dataclass MyDataset(Dataset):# 继承torch工具类的Dataset,进而构造DataLoader进行批量数据读取# 继承Dataset需要实现以下3个函数def __init__(self, dataset):super().__init__()data_x, data_y = zip(*dataset)self.data_x = data_xself.data_y = data_ydef __getitem__(self, item):return self.data_x[item], self.data_y[item]def __len__(self):assert len(self.data_x) == len(self.data_y)return len(self.data_x)class MyModel(nn.Module):# 继承nn.Module构建模型def __init__(self, hidden_size):super(MyModel, self).__init__()self.h1 = nn.Linear(1, hidden_size)self.h2 = nn.Linear(hidden_size, 1)def forward(self, inputs):out1 = self.h1(inputs)out2 = self.h2(F.relu(out1))return out2if __name__ == '__main__':# 可调超参数data_num = 1000tdt_rate = [0.7, 0.2, 0.1]seed = 1hidden_size = 128batch_size = 4lr = 1e-3epoch = 50# 固定随机数setup_seed(seed)# 生成数据total_data = generate_data(data_num)random.shuffle(total_data)# 构建训练集、开发集、测试集train_data, dev_data, test_data = distribute_dataset(total_data, tdt_rate)train_ds = MyDataset(train_data)dev_ds = MyDataset(dev_data)test_ds = MyDataset(test_data)# 构建模型model = MyModel(hidden_size)loss = nn.MSELoss()optimizer = torch.optim.Adam(model.parameters(), lr=lr)# 训练集训练train_loader = DataLoader(train_ds, batch_size=batch_size)for e in range(epoch):total_l = []for x, y in train_loader:# 调整数据形式 x -> [batch_size, 1], y -> [batch_size]x = x.unsqueeze(-1).type(torch.float32)y = y.type(torch.float32)# 均方误差 (预测值 - 真实值)^2l = loss(model(x).squeeze(-1), y)# 邢将军法 |sign(预测值) * 预测值^2 - 真实值^2|# y_ = model(x).squeeze(-1)# sign = torch.sign(y_)# l = torch.abs(sign * y_ ** 2 - y ** 2).sum()# |预测值 - 真实值|# l = torch.abs(model(x).squeeze(-1) - y).sum()# |预测值^3 - 真实值^3|# l = torch.abs(model(x).squeeze(-1) ** 3 - y ** 3).sum()l.backward()optimizer.step()optimizer.zero_grad()total_l.append(l.item())print(f"epoch {e+1}, avg_loss {sum(total_l) / len(total_l)}")# for e in range(epoch):#     # (x, y) -> (y, x) 增加靠近10的数据量,让模型再靠近10的地方拟合地更好#     total_l = []#     for x, y in train_loader:#         x, y = y, x#         # 调整数据形式 x -> [batch_size, 1], y -> [batch_size]#         x = x.unsqueeze(-1).type(torch.float32)#         y = y.type(torch.float32)##         # 均方误差 (预测值 - 真实值)^2#         # l = loss(model(x).squeeze(-1), y)##         # 邢将军法 |sign(预测值) * 预测值^2 - 真实值^2|#         y_ = model(x).squeeze(-1)#         sign = torch.sign(y_)#         l = torch.abs(sign * y_ ** 2 - y ** 2).sum()##         # |预测值 - 真实值|#         # l = torch.abs(model(x).squeeze(-1) - y).sum()##         # |预测值^3 - 真实值^3|#         # l = torch.abs(model(x).squeeze(-1) ** 3 - y ** 3).sum()##         l.backward()#         optimizer.step()#         optimizer.zero_grad()#         total_l.append(l.item())#     print(f"reverse  epoch {e+1}, avg_loss {sum(total_l) / len(total_l)}")# 开发集调超参数dev_loader = DataLoader(dev_ds, batch_size=batch_size)with torch.no_grad():xs, ys, y_s = [], [], []for x, y in dev_loader:xs.extend(list(x.numpy()))ys.extend(list(y.numpy()))x = x.unsqueeze(-1).type(torch.float32)y = y.type(torch.float32)y_ = model(x).squeeze(-1)y_s.extend(list(y_.numpy()))# 排序total = [(a, b, c) for a, b, c in zip(xs, ys, y_s)]total.sort(key=lambda i: i[0])xs, ys, y_s = zip(*total)plt.plot(xs, ys, label='real')plt.plot(xs, y_s, label='pred')plt.legend()plt.title('dev')plt.show()# 测试集测试test_loader = DataLoader(test_ds, batch_size=batch_size)with torch.no_grad():xs, ys, y_s = [], [], []for x, y in test_loader:xs.extend(list(x.numpy()))ys.extend(list(y.numpy()))x = x.unsqueeze(-1).type(torch.float32)y = y.type(torch.float32)y_ = model(x).squeeze(-1)y_s.extend(list(y_.numpy()))# 排序total = [(a, b, c) for a, b, c in zip(xs, ys, y_s)]total.sort(key=lambda i: i[0])xs, ys, y_s = zip(*total)plt.plot(xs, ys, label='real')plt.plot(xs, y_s, label='pred')plt.legend()plt.title('test')plt.show()

神经网络的学习能力很强,获得不错的拟合效果。

讨论

新的损失函数

上述代码中的loss选用的是均方误差——(预测值 - 真实值) ^2,邢将军提出了新的损失函数——|sign(预测值) * 预测值^2 - 真实值^2|,其中sign是符号函数,公式如下:

损失函数描述预测值和真实值之间的误差,当预测值趋近于真实值时,损失函数应越来越小。预测值^2 - 真实值^2 满足损失函数的基本定义。

损失函数不能为负数,网络的优化目标是最小化损失函数,如果损失可为负数,网络将把损失推向负无穷,所以加上绝对值,成为**|预测值^2 - 真实值^2|**。

在此次拟合中,真实值的取值范围是[0, 10],永为正数。当预测值趋近于真实值的相反数时,损失依然在不断减小。如果初始梯度方向是向着真实值相反数的方向,最终就会导致出现关于x轴对称的拟合曲线出现。所以当预测值趋近于真实值的相反数时,要让损失变大。这里使用sign函数,当预测值为负值时,将产生更大的损失。最终的损失函数为**|sign(预测值) * 预测值^2 - 真实值^2|**。

代码实现如下:

y_ = model(x).squeeze(-1)
sign = torch.sign(y_)
l = torch.abs(sign * y_ ** 2 - y ** 2).sum()

效果图如下,除了尾部数据仍然有偏差,拟合效果大幅提升。

基于邢将军的启发,我们可以设计更多损失函数,这些损失函数都表现不错。

  • | 预测值 - 真实值|
  • |sign(预测值) * 预测值^2 - 真实值^2|
  • | 预测值^3 - 真实值^3|

尾部偏差

sr同学认为预拟合的曲线在越靠近10的地方,|斜率|越来越大,最终极限为无穷大。在数据生成阶段,x是等距采样的,而y值的变化并非均匀。越靠近10,y值的变化越明显,特征越稀疏,不利于模型学习。

sr同学认为 x^2 + y^2 = 100 中,x与y是对称的,也就是说x与y可以互换。如果将生成的数据(x, y)对调成(y, x),则原来靠近0的地方数据更密集,现在靠近10的地方数据更密集。让模型再学习(y, x),尾部的偏差就能够拟合地更好。

代码很简单,将原训练代码的x与y互换即可。

for e in range(epoch):# (x, y) -> (y, x) 增加靠近10的数据量,让模型再靠近10的地方拟合地更好total_l = []for x, y in train_loader:# 对调x, y = y, x# 调整数据形式 x -> [batch_size, 1], y -> [batch_size]x = x.unsqueeze(-1).type(torch.float32)y = y.type(torch.float32)# 均方误差 (预测值 - 真实值)^2# l = loss(model(x).squeeze(-1), y)# 邢将军法 |sign(预测值) * 预测值^2 - 真实值^2|y_ = model(x).squeeze(-1)sign = torch.sign(y_)l = torch.abs(sign * y_ ** 2 - y ** 2).sum()# |预测值 - 真实值|# l = torch.abs(model(x).squeeze(-1) - y).sum()# |预测值^3 - 真实值^3|# l = torch.abs(model(x).squeeze(-1) ** 3 - y ** 3).sum()l.backward()optimizer.step()optimizer.zero_grad()total_l.append(l.item())print(f"reverse  epoch {e+1}, avg_loss {sum(total_l) / len(total_l)}")

效果图如下,尾部数据也得到有效的拟合。

总结

  1. 经验上,对于回归问题,我们倾向于使用均方误差作为损失函数;对于分类问题,我们倾向于使用交叉熵作为损失函数。然而,我们可以根据实际问题,设计出更好的损失函数,让模型收敛更快,效果更好。
  2. 神经网络的学习能力很强,但需要大量数据多次训练。如果模型在某些地方表现的不够好,增加这方面的数据,模型就能自动学习到特征,表现得更好。

神经网络拟合曲线及讨论相关推荐

  1. 使用神经网络拟合曲线(MATLAB/Python)

    前言 神经网络通常用于分类任务,也可以用于回归任务.使用一个含有隐层的神经网络可以很轻松地拟合出非线性曲线.下面是几个示例,包含matlab的和python的,都很简单. 实例1 首先,生成正弦曲线, ...

  2. 神经网络拟合是什么意思,神经网络拟合二次曲线

    BP神经网络可以用于拟合函数吗 可以.既然是函数拟合,那么事先就已经有函数表达式了.拟合的只是函数表达式中未知的参数.用神经网络对函数进行拟合,输出的就是未知参数的高精近似值.人工神经网络就是模拟人思 ...

  3. 一文读懂神经网络(附解读案例)

    "你的大脑并不产生思想.你的思想塑造了神经网络."--Deepak Chopra 引文 J. Nocedal y S. Wright, "Numerical optimi ...

  4. 独家 | 一文读懂神经网络(附解读案例)

    作者:Matthew Stewart 翻译:车前子 校对:陈丹 本文约5500字,建议阅读12分钟. 本文的知识将提供一个强有力的基础,带你入门神经网络的性能,应用于深度学习应用. "你的大 ...

  5. CICC科普栏目|神经网络浅讲:从神经元到深度学习

    图1 人脑神经网络 来源: 计算机的潜意识 摘要:神经网络是一门重要的机器学习技术.它是目前最为火热的研究方向–深度学习的基础. 神经网络是一门重要的机器学习技术.它是目前最为火热的研究方向–深度学习 ...

  6. 知物由学 | 未来安全隐患:AI的软肋——故意欺骗神经网络

     本文由  网易云 发布. "知物由学"是网易云易盾打造的一个品牌栏目,词语出自汉·王充<论衡·实知>.人,能力有高下之分,学习才知道事物的道理,而后才有智慧,不去求问 ...

  7. grad在python什么模块_深度学习(Deep Learning)基础概念1:神经网络基础介绍及一层神经网络的python实现...

    此专栏文章随时更新编辑,如果你看到的文章还没写完,那么多半是作者正在更新或者上一次没有更新完,请耐心等待,正常的频率是每天更新一篇文章. 该文章是"深度学习(Deep Learning)&q ...

  8. 第五章 卷积神经网络(CNN)

    文章目录 5.1 卷积神经网络的组成层 5.2 卷积如何检测边缘信息? 5.3 卷积层中的几个基本参数? 5.3.1 卷积核大小 5.3.2 卷积核的步长 5.3.3 边缘填充 5.3.4 输入和输出 ...

  9. 卷积神经网络图像分类识别

    卷积神经网络和图像分类识别Andrew Kirillov 著 Conmajia 译 2019 年 1 月 15 日原文发表于 CodeProject(2018 年 10 月 28 日). 中文版有小幅 ...

最新文章

  1. 基于Quartus II软件FPGA与PC之间的串行通信系统开发(9000+字)
  2. Spring Cloud Alibaba - 10 Ribbon 自定义负载均衡策略(权重算法)
  3. drtek收音机使用说明_一百年前的便携式矿石收音机长啥样?这台1919年产品给你答案...
  4. TypeScript的书写规范(TSLint)配置修改
  5. 关联规则java代码_重量挖掘关联规则挖掘方法,哪个大神可以将以下伪代码转换为Java代码?...
  6. LAMP源代码编译整理
  7. 【BZOJ3036】绿豆蛙的归宿 拓补排序+概率
  8. Criteria和DetachedCriteria区别应用
  9. 构造体中变量后面的冒号_类型在变量前面还是后面,终于有答案了
  10. C语言Dialogbox添加图片,c# dll c 类_dialogboxparam_msg结构
  11. Codeforces Round #512 (Div. 2) - D. Vasya and Triangle (皮克公式)
  12. 极简主义、人工智能与Readhub的产品哲学
  13. 10天背诵英文10000单词
  14. electron打开系统默认浏览器
  15. QT:黑白棋的吃子规则(七)
  16. android 播放wav代码,播放简短的.wav文件 - Android
  17. nagios 的安装
  18. 游戏开发与实现南梦宫笔记
  19. Charles软件破解方法
  20. Elasticsearch的这几个概念你还不知道啥意思呢?

热门文章

  1. 【笔记一】pycharm写入excel学习笔记
  2. MassGrid有奖征文活动
  3. OC-------包装类
  4. R语言which函数详解以及Rcpp改写
  5. 贵州茅台——病毒性营销方案
  6. 尚济 形意拳经解[七]内功经解
  7. 黑马Drools学习笔记(三)——Drools规则属性
  8. 【Linux进程概念——下】验证进程地址空间的基本排布 | 理解进程地址空间 | 进程地址空间如何映射至物理内存(页表的引出) | 为什么要存在进程地址空间 | Linux2.6内核进程调度队列
  9. 阿里云ECS安全增强通用型g7t服务器支持SGX加密计算由TPM/TCM芯片可信启动
  10. STM32F103标准固件库寄存器及常用函数整理