当前位置: 首页 > news >正文

《一文读懂PyTorch核心模块:开启深度学习之旅》

《一文读懂PyTorch核心模块:开启深度学习之旅》

  • 一、PyTorch 入门:深度学习的得力助手
  • 二、核心模块概览:构建深度学习大厦的基石
  • 三、torch:基础功能担当
    • (一)张量操作:多维数组的神奇变换
    • (二)自动微分:梯度求解的幕后英雄
    • (三)设备管理:CPU 与 GPU 的高效调度
  • 四、torch.nn:神经网络的 “魔法工坊”
    • (一)神经网络模块:层层堆叠搭建智能模型
    • (二)损失函数:模型优化的 “指南针”
  • 五、torch.optim:模型优化的 “加速引擎”
    • (一)优化算法:梯度下降的 “升级版”
    • (二)学习率调度器:精细调整学习步伐
  • 六、torch.utils.data:数据处理的 “流水线”
    • (一)数据集:定制专属数据来源
    • (二)数据加载器:高效批量输送数据 “燃料”
  • 七、torchvision:计算机视觉的 “百宝箱”
    • (一)数据集:图像数据的便捷获取
    • (二)预训练模型:站在巨人肩膀上创新
  • 八、案例实战:用核心模块打造图像分类器
    • (一)项目准备:加载数据与必要模块导入
    • (二)模型构建:精心雕琢神经网络架构

一、PyTorch 入门:深度学习的得力助手

在当今的科技领域,深度学习无疑是最炙手可热的研究方向之一,它正以前所未有的速度改变着我们的生活。从智能语音助手的精准回应,到自动驾驶汽车的安全行驶;从医疗影像的精准诊断,到金融风险的智能预测,深度学习的应用场景无处不在。而在深度学习的蓬勃发展背后,PyTorch 作为一款极具影响力的开源深度学习框架,扮演着至关重要的角色。
PyTorch 由 Facebook 的人工智能研究团队(FAIR)开发,自 2017 年发布以来,迅速在学术界和工业界获得了广泛的认可与应用。它以其简洁优雅的设计、动态计算图的特性、与 Python 无缝融合的优势,以及强大的社区支持,为深度学习开发者们提供了一个高效且易用的工具。无论是刚刚踏入深度学习领域的初学者,还是经验丰富的专业研究人员,PyTorch 都能满足他们的需求,助力他们将创新的想法快速转化为实际的模型。
在学术研究中,PyTorch 已成为众多研究人员的首选工具。根据 arXiv 上的论文统计数据,近年来使用 PyTorch 的论文数量呈现出爆发式增长,其在顶会中的引用率不断攀升,许多前沿的研究成果都基于 PyTorch 实现。在工业界,各大科技公司纷纷将 PyTorch 应用于实际产品的开发中,从图像识别、自然语言处理到推荐系统等诸多领域,PyTorch 都展现出了卓越的性能。
接下来,让我们一同深入探索 PyTorch 的核心模块,揭开其强大功能的神秘面纱,开启深度学习的精彩之旅。

二、核心模块概览:构建深度学习大厦的基石

PyTorch 的核心模块宛如一座宏伟建筑的基石,它们相互协作,共同支撑起深度学习模型从构建、训练到部署的整个流程。这些模块涵盖了张量运算、神经网络构建、优化算法、数据处理等多个关键领域,每一个模块都发挥着不可或缺的作用。
首先是 torch 模块,它作为 PyTorch 的基础核心,提供了张量这一基本数据结构,如同建筑中的砖块。张量支持各种数学运算,无论是简单的加减乘除,还是复杂的线性代数操作,都能轻松应对。同时,它还具备自动微分功能,为反向传播算法提供了有力支持,能够自动计算梯度,这就像是给模型训练安装了一台智能导航仪,指引模型朝着最优的方向前进。并且,torch 模块还负责设备管理,使得张量能够在 CPU 和 GPU 之间灵活迁移,充分利用 GPU 的强大计算能力,大幅提升计算效率,如同为建筑施工配备了高效的起重机,加速工程进度。
torch.nn 模块则专注于神经网络的构建,是模型的 “设计师”。它提供了丰富多样的神经网络层,如卷积层、池化层、全连接层等,这些层就像是建筑中的不同结构部件,通过合理组合可以搭建出各种复杂精巧的网络架构。此外,常见的激活函数和归一化层也包含其中,激活函数为模型引入非线性特性,使其能够学习到复杂的数据模式,而归一化层则有助于稳定模型训练,提高收敛速度。同时,一系列损失函数也在这个模块中,它们如同建筑的质量评判标准,用于衡量模型预测结果与真实标签之间的差异,为模型优化提供目标导向。
当涉及到模型的训练优化时,torch.optim 模块就派上了用场,它是模型的 “训练师”。这个模块提供了多种优化算法,如随机梯度下降(SGD)、Adam、RMSprop 等,这些算法就像是不同风格的教练,各有其训练策略,能够根据模型的特点和数据的特性,精准地调整模型参数,让模型在训练过程中不断提升性能,逐步接近最优解。
数据是深度学习的 “燃料”,而 torch.utils.data 模块则承担着数据处理的重任,它是数据的 “搬运工” 和 “加工师”。通过 Dataset 类,我们可以轻松自定义数据集,将各种原始数据整理成模型能够读取的格式。DataLoader 则负责批量加载数据,支持多线程加载,就像高效的输送带,源源不断地将数据输送给模型,并且还能对数据进行打乱、分组等操作,确保模型在训练过程中充分接触到不同的数据样本,避免过拟合。
在计算机视觉领域,torchvision 模块大放异彩,它为图像相关的深度学习任务提供了一站式解决方案。一方面,它内置了诸多常用的计算机视觉数据集,如 MNIST、CIFAR-10、ImageNet 等,这些数据集就像是精心准备的素材库,为模型训练提供了丰富的图像资源。另一方面,一系列预训练模型,如 ResNet、VGG、AlexNet 等,宛如已经搭建好的半成品建筑,我们可以基于这些模型进行迁移学习,快速应用到自己的任务中,节省大量的训练时间和计算资源。同时,它还提供了便捷的数据变换功能,能够对图像进行大小调整、裁剪、归一化等操作,确保数据符合模型的输入要求。
torch.jit 模块则专注于模型的部署环节,它像是一位 “翻译官”,将 Python 模型转换为 TorchScript 模型。通过脚本化和追踪技术,它能够提高模型的执行效率,并且支持跨平台部署,让模型能够在不同的环境中稳定运行,真正将深度学习的成果推向实际应用的舞台。
这些核心模块相互配合,紧密协作,为深度学习开发者们提供了一个强大且便捷的工具集,使得我们能够在各个领域中充分发挥深度学习的潜力,创造出更多具有价值的应用。

三、torch:基础功能担当

(一)张量操作:多维数组的神奇变换

在 PyTorch 中,张量(Tensor)是最为基础且核心的数据结构,它就如同建筑中的砖块,是搭建深度学习模型大厦的基石。张量可以被视为是一个多维数组,涵盖了从简单的标量(零维张量)、向量(一维张量),到矩阵(二维张量),乃至更高维度的数组形式,能够灵活地表示各种复杂的数据。
创建张量的方式丰富多样,满足了不同场景下的需求。比如,我们可以使用 torch.tensor() 函数,通过传入 Python 的列表、元组或 NumPy 数组等数据结构来创建张量,就像是将原材料加工成统一规格的砖块。示例代码如下:

import torch# 通过列表创建一维张量
vector = torch.tensor([1, 2, 3])
print(vector)  # 通过列表的列表创建二维张量,类似矩阵
matrix = torch.tensor([[1, 2], [3, 4]])
print(matrix)  # 利用 NumPy 数组创建张量,实现二者的无缝对接
import numpy as np
numpy_array = np.array([[5, 6], [7, 8]])
tensor_from_numpy = torch.tensor(numpy_array)
print(tensor_from_numpy)  

PyTorch 还提供了一系列便捷的函数来创建特定形状和数值分布的张量。例如,torch.zeros() 可以创建全零张量,常用于初始化模型参数,为模型搭建提供初始的 “空白画布”;torch.ones() 则能生成全一张量,在某些需要初始化为固定值的场景大有用处;torch.randn() 能够从标准正态分布中随机采样生成张量,为模型训练引入随机性,避免陷入局部最优,就像是为模型训练的 “探索之旅” 提供了多样的路径选择。以下是具体示例:

# 创建一个形状为 (3, 3) 的全零张量
zeros_tensor = torch.zeros((3, 3))  
print(zeros_tensor)# 生成一个形状为 (2, 4) 的全一张量
ones_tensor = torch.ones((2, 4))  
print(ones_tensor)# 从标准正态分布中随机生成一个形状为 (5, 5) 的张量
randn_tensor = torch.randn((5, 5))  
print(randn_tensor)

对于已创建的张量,我们可以像操作多维数组一样对其进行索引、切片操作,精准地获取或修改张量中的部分数据,满足模型在数据处理过程中的各种精细需求。同时,张量支持丰富的数学运算,无论是简单的加减乘除四则运算,还是复杂的线性代数操作,如矩阵乘法(torch.mm() 或 @ 运算符)、向量点积(torch.dot())、张量的转置(.T)等,都能高效完成。这使得我们在构建模型时,可以方便地对数据进行各种变换和处理,就如同熟练的工匠运用工具对砖块进行雕琢、拼接,打造出精巧的结构。示例如下:

# 定义两个张量
a = torch.tensor([[1, 2], [3, 4]])
b = torch.tensor([[5, 6], [7, 8]])# 张量加法
c = a + b
print("加法结果:", c)  # 张量乘法(对应元素相乘)
d = a * b
print("对应元素相乘结果:", d)  # 矩阵乘法
e = torch.mm(a, b)
print("矩阵乘法结果:", e)  # 向量点积
vector_a = torch.tensor([1, 2, 3])
vector_b = torch.tensor([4, 5, 6])
dot_product = torch.dot(vector_a, vector_b)
print("向量点积结果:", dot_product)  # 张量转置
transposed_a = a.T
print("转置结果:", transposed_a)  

值得一提的是,PyTorch 的张量运算在 GPU 上能够实现显著的加速。当系统配备 NVIDIA GPU 且安装了相应的 CUDA 驱动时,只需简单地将张量转移到 GPU 上,后续的计算操作就能利用 GPU 的强大并行计算能力,大幅缩短计算时间,如同为模型训练配备了一台超级引擎,让模型 “飞速奔跑”。示例代码展示了如何轻松实现 CPU 到 GPU 的切换:

# 检查 GPU 是否可用
if torch.cuda.is_available():device = torch.device('cuda')
else:device = torch.device('cpu')# 创建张量并移动到 GPU
tensor = torch.randn((1000, 1000)).to(device)
# 在 GPU 上进行矩阵乘法运算
result = torch.mm(tensor, tensor)

(二)自动微分:梯度求解的幕后英雄

自动微分机制是 PyTorch 的一大核心亮点,它为深度学习模型的训练优化提供了强大的支持,宛如模型训练过程中的智能导航仪,精准指引模型参数调整的方向。
在深度学习中,模型的训练本质上是一个优化问题,我们需要通过不断调整模型的参数,使得模型的预测结果尽可能地接近真实标签。而要实现这一目标,关键在于能够高效、准确地计算损失函数相对于模型参数的梯度。PyTorch 的自动微分功能正是基于这一需求而设计,它能够自动追踪张量的所有操作,并构建一个动态的计算图(Computational Graph),记录从输入数据到输出结果的完整计算流程。
在这个计算图中,每个张量操作都被视为一个节点,而张量之间的依赖关系则构成了边。当我们需要计算梯度时,只需调用 backward() 方法,自动微分机制就会沿着计算图的反向路径,依据链式法则,自动且精确地计算出每个参与运算的张量的梯度。这一过程就像是沿着一条精心铺设的回溯轨道,从最终的输出结果一步步回溯到最初的输入,将沿途的梯度信息一一收集起来。
让我们通过一个简单的线性回归示例来深入理解自动微分的工作原理。假设我们有一组输入数据 x 和对应的真实标签 y,模型的预测值 y_pred 由线性函数 y_pred = w * x + b 给出,其中 w 和 b 是需要学习的模型参数,我们的目标是通过最小化预测值与真实标签之间的均方误差损失函数 loss = ((y_pred - y) ** 2).mean() 来调整 w 和 b 的值。
在 PyTorch 中,实现上述过程的代码如下:

import torch# 模拟输入数据和真实标签
x = torch.tensor([1., 2., 3., 4.], requires_grad=False)
y = torch.tensor([2., 4., 6., 8.], requires_grad=False)# 初始化模型参数,设置 requires_grad=True 以追踪梯度
w = torch.tensor(0.5, requires_grad=True)
b = torch.tensor(0.5, requires_grad=True)# 前向传播计算预测值
y_pred = w * x + b# 计算损失函数
loss = ((y_pred - y) ** 2).mean()# 反向传播计算梯度
loss.backward()# 输出梯度值
print("w 的梯度:", w.grad)
print("b 的梯度:", b.grad)

在上述代码中,我们首先定义了输入数据 x 和真实标签 y,并将模型参数 w 和 b 的 requires_grad 属性设置为 True,告知 PyTorch 需要追踪这些张量的操作以计算梯度。接着进行前向传播,得到预测值 y_pred 并计算损失函数 loss。最后,调用 loss.backward() 触发反向传播过程,PyTorch 会自动计算出 w 和 b 的梯度,并将结果存储在它们的 .grad 属性中。
自动微分的强大之处不仅在于其自动化的计算过程,还在于它能够与各种复杂的模型结构和计算流程无缝结合。无论是简单的多层感知机,还是复杂的卷积神经网络、循环神经网络,PyTorch 的自动微分机制都能准确无误地计算出梯度,为模型的训练提供坚实的基础。这使得研究者和开发者们能够将更多的精力聚焦于模型架构的创新和应用场景的拓展,而无需在繁琐的梯度计算细节上耗费大量时间。

(三)设备管理:CPU 与 GPU 的高效调度

在深度学习的计算任务中,合理地管理计算设备,充分发挥 CPU 和 GPU 的优势,是提升模型训练效率的关键一环。PyTorch 提供了简洁而强大的设备管理功能,让我们能够轻松地在 CPU 和 GPU 之间进行切换,实现高效的计算资源调度。
首先,我们可以通过 torch.cuda.is_available() 函数快速查询当前系统是否配备了可用的 NVIDIA GPU 以及相应的 CUDA 驱动。这一函数就像是一位贴心的 “硬件

相关文章:

《一文读懂PyTorch核心模块:开启深度学习之旅》

《一文读懂PyTorch核心模块:开启深度学习之旅》 一、PyTorch 入门:深度学习的得力助手二、核心模块概览:构建深度学习大厦的基石三、torch:基础功能担当(一)张量操作:多维数组的神奇变换(二)自动微分:梯度求解的幕后英雄(三)设备管理:CPU 与 GPU 的高效调度四、to…...

摆脱Zotero存储限制:WebDAV结合内网穿透打造个人文献管理云平台

文章目录 前言一、Zotero安装教程二、群晖NAS WebDAV设置三、Zotero设置四、使用公网地址同步Zotero文献库五、使用永久固定公网地址同步Zotero文献库 前言 如果你是科研工作者、学生或者任何需要频繁处理大量学术资料的人士,你一定对如何高效管理和引用文献感到头…...

Flutter封装一个三方ViewPager学习

Flutter如何实现一个增强的 PageView,支持自定义页面切换动画。 前置知识点学习 CrossAxisAlignment CrossAxisAlignment 是 Flutter 中用于控制布局子组件在交叉轴(cross axis)方向上的对齐方式的一个枚举类。它主要在 Flex 布局模型中使…...

服务器数据恢复—离线盘数超过热备盘数导致raidz阵列崩溃的数据恢复

服务器数据恢复环境&故障: 一台配有32块硬盘的服务器在运行过程中突然崩溃不可用。经过初步检测,基本上确定服务器硬件不存在物理故障。管理员重启服务器后问题依旧。需要恢复该服务器中的数据。 服务器数据恢复环境: 1、将服务器中硬盘…...

nginx-nginx的缓存集成

缓存的概念 缓存就是数据交换的缓冲区,被称作cache,访用户想要获取数据时,就会先从缓存中去查询数据,如果缓存中有就会直接返回给用户,若果缓存中没有,则会发出请求从服务器中重新查询数据,将数…...

【Vim Masterclass 笔记01】Section 1:Course Overview + Section 2:Vim Quickstart

文章目录 Section 1:Course Introduction 课程概述S01L01 Course Overview 课程简介课程概要 S01L02 Course Download 课程资源下载S01L03 What Vim Is and Why You Should Learn It 何为 Vim?学来干啥?1 何为 Vim2 为何学 Vim Section 2&…...

【数据库系列】Spring Boot 中使用 MyBatis 详细指南

一、基础介绍 1.1 MyBatis MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 P…...

Azure Airflow 中配置错误可能会使整个集群受到攻击

网络安全研究人员在 Microsoft 的 Azure 数据工厂 Apache Airflow 中发现了三个安全漏洞,如果成功利用这些漏洞,攻击者可能会获得执行各种隐蔽操作的能力,包括数据泄露和恶意软件部署。 “利用这些漏洞可能允许攻击者以影子管理员的身份获得…...

Python跨年烟花

目录 系列文章 写在前面 技术需求 完整代码 下载代码 代码分析 1. 程序初始化与显示设置 2. 烟花类 (Firework) 3. 粒子类 (Particle) 4. 痕迹类 (Trail) 5. 烟花更新与显示 6. 主函数 (fire) 7. 游戏循环 8. 总结 注意事项 写在后面 系列文章 序号直达链接爱…...

【代码】Python|Windows 批量尝试密码去打开加密的 Word 文档(docx和doc)

文章目录 前言完整代码Githubdocxdoc 代码解释1. msoffcrypto 方法(用于解密 .docx 文件)read_secret_word_file 函数密码生成与解密尝试try_decrypt_file 函数 2. comtypes 方法(用于解密 .doc 文件)read_secret_word_file 函数注…...

java开发中注解汇总​​

注解作用位置注意mybatis Data Getter Setter ToString EqualsAndHashCode AllArgsConstructor NoArgsConstructor Data 代替:无参构造,get,set,toString,hashCode,equals Getter Setter 可放在类和方法上&…...

C# 设计模式(结构型模式):外观模式

C# 设计模式(结构型模式):外观模式 (Facade Pattern) 在复杂系统中,往往会涉及到多个子系统、模块和类。这些子系统的接口和功能可能会让使用者感到困惑和复杂。在这种情况下,我们可以使用外观模式(Facade…...

PowerShell 常见问题解答

PowerShell 是微软开发的一种功能强大的命令行界面和脚本语言,广泛应用于系统管理和自动化任务。以下是一些使用 PowerShell 时常见的问题及其解决方法。 什么是 PowerShell? PowerShell 是基于 .NET 的命令行界面(CLI)和脚本语言…...

计算机网络 (15)宽带接入技术

前言 计算机网络宽带接入技术是指通过高速、大容量的通信信道或网络,实现用户与互联网或其他通信网络之间的高速连接。 一、宽带接入技术的定义与特点 定义:宽带接入技术是指能够传输大量数据的通信信道或网络,其传输速度通常较高&#xff0c…...

前端Python应用指南(六)构建RESTful API:使用Flask和Django实现用户认证与授权

《写给前端的python应用指南》系列: (一)快速构建 Web 服务器 - Flask vs Node.js 对比(二)深入Flask:理解Flask的应用结构与模块化设计(三)Django vs Flask:哪种框架适…...

【Unity3D】基于UGUI——简易版 UI框架

https://github.com/AMikeW/BStandShaderResources/blob/master/milk_UIFramework.unitypackage UI框架支持如下功能: 1、层级控制 2、支持面板多次打开时,隐藏前一个打开的面板,当关闭面板时,能够恢复前一个打开面板状态 3、支…...

算法排序算法

文章目录 快速排序[leetcode 215数组中的第K个最大元素](https://leetcode.cn/problems/kth-largest-element-in-an-array/)分析题解快速排序 桶排序[leetcode 347 前K个高频元素](https://leetcode.cn/problems/top-k-frequent-elements/)分析题解 快速排序 leetcode 215数组…...

第3章 总线

总线的定义 为多个部件 分时共享 公共信息传送线路。 系统之间、模块之间、芯片内部用来传递信息信号线集合。 共享 总线上可连接多个部件 各部件间相互交换信息 都可通过总线来。 分时 同一时刻 总线上只能传 一个部件信息。 采用标准总线的优点 简化系统软硬件设计 从硬件角度…...

手机实时提取SIM卡打电话的信令声音-双卡手机来电如何获取哪一个卡的来电

手机实时提取SIM卡打电话的信令声音 --双卡手机来电如何获取哪一个卡的来电 一、前言 前面的篇章《手机实时提取SIM卡打电话的信令声音-智能拨号器的双SIM卡切换方案》中,我们论述了局域网SIP坐席通过手机外呼出去时,手机中主副卡的呼叫调度策略。 但…...

共阳极LED的控制与短路问题解析

共阳极LED的控制与短路问题解析 在电子电路中,LED(发光二极管)是最常见的元件之一。LED的连接方式分为共阳极和共阴极,不同的连接方式决定了LED的控制逻辑。本文将重点讲解共阳极LED的工作原理,并解答“为什么给1不会…...

微信小程序之bind和catch

这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...

Angular微前端架构:Module Federation + ngx-build-plus (Webpack)

以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

LLMs 系列实操科普(1)

写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...

tomcat指定使用的jdk版本

说明 有时候需要对tomcat配置指定的jdk版本号,此时,我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...

拟合问题处理

在机器学习中,核心任务通常围绕模型训练和性能提升展开,但你提到的 “优化训练数据解决过拟合” 和 “提升泛化性能解决欠拟合” 需要结合更准确的概念进行梳理。以下是对机器学习核心任务的系统复习和修正: 一、机器学习的核心任务框架 机…...

手动给中文分词和 直接用神经网络RNN做有什么区别

手动分词和基于神经网络(如 RNN)的自动分词在原理、实现方式和效果上有显著差异,以下是核心对比: 1. 实现原理对比 对比维度手动分词(规则 / 词典驱动)神经网络 RNN 分词(数据驱动&#xff09…...

EC2安装WebRTC sdk-c环境、构建、编译

1、登录新的ec2实例,证书可以跟之前的实例用一个: ssh -v -i ~/Documents/cert/qa.pem ec2-user70.xxx.165.xxx 2、按照sdk-c demo中readme的描述开始安装环境: https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-c 2…...

Three.js + Vue3 加载GLB模型项目代码详解

本说明结合 src/App.vue 代码,详细解释如何在 Vue3 项目中用 three.js 加载并显示 glb 模型。 1. 依赖与插件导入 import {onMounted, onUnmounted } from vue import * as THREE from three import Stats from stats.js import {OrbitControls } from three/examples/jsm/co…...