从顶点到最低点的机器学习模型的部署和生产— I
了解从数据采集到模型部署的全过程。
数据科学——商业事务
构建机器学习模型的典型过程涉及数据收集:用于从数据库(ETL)或任何其他来源获取数据的过程,然后分析数据,从数据中设计和选择特征,以在数据上构建模型,最后分析模型结果。但是一旦你建立了模型,你会怎么做呢?在哪里以及如何利用它来获取利益?
碎成碎片
有收集数据的方法,或者我应该说有许多数据来源。有时需要经过一个漫长的过程(ETL、数据库)才能获得与业务问题相关的所需数据,有时客户会以 excel、CSV 或任何其他文件格式向您提供数据,但后者的可能性很小。大多数情况下,您需要连接到数据库,或者遵循一个完整的 DBM 过程,将数据发送到仓库,然后进行分析并构建模型。我会在另一篇文章中详细讲述这个过程。
一旦你清理好数据并准备好。我们从分析数据开始,通常从发现基本的见解和一些健全性检查(数据类型、汇总统计等)开始。在数据科学中,这个术语叫做探索性数据分析(EDA)。这可以在获取数据时完成,即在作为数据源的系统中完成,也可以通过 Python、R 等工具完成。我更喜欢 Python。
从这里开始,我们走向流程的业务端。从特征工程开始,涉及缺失值插补、离群点检测、变量变换等。特征选择是从数据集中的所有给定特征(特征=变量)中选择最相关的特征,也是该过程的关键部分。这两个过程给了我们所谓的“特征化”数据。特征工程和特征选择是一个很大且费时的话题,因此,集中讨论过程是很重要的。
点睛之笔
我们漫步到模型建立和评估阶段,已经获得了“特征化”的数据,这仅涉及建立不同的机器学习模型并选择给出最佳结果的模型。(培训和测试阶段)。一旦我们得到了模型,之后你会做什么?
你已经有了团队认可的最终模型,现在你需要生产这个模型。生产化是什么意思?这意味着将模型部署到生产系统中,部署到实时场景中。在研究阶段已经完成了所有必要的测试。我们需要部署这个模型来应用我们的研究。
给定数据,我们离线训练我们的模型。该模型部署在实时场景中,它从现实世界中获取连续输入,并根据模型类型(回归、分类等)给出连续输出。
在这一点上,我认为我们讨论机器学习管道和机器学习模型之间的区别是很重要的,因为我们不仅部署模型,而且部署整个管道。
机器学习管道包含了从数据中获得预测所需的所有步骤(我上面提到的步骤)。然而,机器学习模型只是这个管道的一部分(模型构建部分)。虽然模型描述了使用数据中的模式来生成预测的特定算法或方法,但管道概述了机器学习过程中涉及的所有步骤,从收集数据到获取预测。
你如何把你的模型放到一个生产环境中,所有必要的细节将在后续的文章中讨论。
机器学习模型的部署和生产—从顶点到最低点— II
建造机器学习家园的砖块和混凝土:系统和架构。
机器学习系统是一个包括整个基础设施(包括从 RAM 到正常平台构建设备的所有硬件的基础设施)的实体,然后它包括一个操作系统,该操作系统为我们的机器学习任务提供动力,并通过提供一个强大的背景使它变得更简单。它还包括所有的应用程序、软件和工具。它渗透到所有的配置方面,如兼容性,以及数据和一点一点地记录工作流程等部分。
机器基础设施包含了机器学习工作流程的几乎每个阶段。为了训练、测试和部署机器学习模型,您需要来自数据科学家、数据工程师、软件编程工程师和 DevOps 工程师的服务。该基础设施允许来自所有这些领域的人员进行协作,并使他们能够为项目的端到端执行进行关联。
工具和平台的一些例子是 AWS(亚马逊网络服务,谷歌云,微软 Azure 机器学习工作室,kube flow:Kubernetes 的机器学习工具包。
架构处理这些组件的安排(上面讨论过的事情),并且还处理它们必须如何与它们交互。可以把它想象成建造一个机器学习之家,砖块、混凝土、铁是基础设施、应用程序等不可或缺的组成部分。建筑通过使用这些材料来塑造我们的家。类似地,这里的架构提供了这些组件之间的交互。
版本化和再现性:“IT”IT 术语
我们先来了解一下什么是版本化和可复制性。
版本控制:
这是常见的软件和 It 实践,您创建和管理一个产品的多个版本,所有这些版本具有相同的一般功能,但是经过改进、升级或定制。
在机器学习中,为给定的数据建立不同的模型,我们通过版本控制工具如 DVC 和 Git 来跟踪。版本控制将跟踪在每个阶段对模型所做的变更,并保留一个存储库。
再现性:
在研究阶段,我们建立机器学习模型的阶段。一旦模型被测试,然后才被部署。部署后,该模型被假定用于构建该模型的相同数据,并预期给出相同的结果。我们部署整个模型管道,而不仅仅是算法。可再现性确保了复制模型的能力,使得给定相同的原始数据作为输入,两个模型返回相同的输出(相同的数据意味着相同的特征等)。
让我来解释一下是什么影响了再现性,这样可以帮助你更好地理解。假设你之前建立的模型给出了 20%的误差。假设您的数据库已经更改,并且当您尝试再次构建模型时,您不知道数据库正在向模型中注入额外的定型数据,因此导致了额外的错误。
有很多因素会影响再现性。因此,这种版本化和版本控制有助于我们跟踪它。大多数情况下,它是在模型改进过程中出现的。
从再现性到配置问题到数据问题,还有各种其他系统挑战。在体系结构中,有一些关键点,如可再现性、可伸缩性:模型为许多人服务的能力,以及其他必须牢记在心的点。
因此,撰写本文主要是为了让您深入了解部署中使用的基本术语。我希望你喜欢这篇文章。
部署可能很容易——使用 Amazon ec2 部署图像检测 FastAPI API 的数据科学家指南
科迪·布莱克在 Unsplash 上的照片
使用亚马逊 EC2+Pytorch+Fastapi 和 Docker
就在最近,我写了一篇关于 FastAPI 的简单的教程,内容是关于简化和理解 API 如何工作,以及使用框架创建一个简单的 API。
那个帖子得到了相当好的回应,但是被问得最多的问题是 如何在 ec2 上部署 FastAPI API,以及如何使用图像数据而不是简单的字符串、整数和浮点数作为 API 的输入。
我在网上搜索了一下,但是我能找到的只是一些不太熟悉的文档和人们使用 NGINX 或 ECS 进行部署的许多不同方式。对我来说,这些都不是特别伟大或完整的。
所以,我试着在 FastAPI 文档的帮助下自己做这件事。在本帖中,我们将主要关注四件事:
- 设置 Amazon 实例
- 创建用于对象检测的 FastAPI API
- 使用 Docker 部署 FastAPI
- 带有用户界面的端到端应用
那么,事不宜迟,我们开始吧。
你可以跳过任何你认为你精通的部分,尽管我希望你浏览整篇文章,尽管它可能很长,因为概念之间有很多相互联系。
1.设置 Amazon 实例
在开始使用 Amazon ec2 实例之前,我们需要设置一个实例。你可能需要用你的电子邮件 ID 注册,并在 AWS 网站上设置支付信息。就像单点登录一样工作。从这里开始,我将假设您有一个 AWS 帐户,因此我将解释接下来的重要部分,以便您可以继续操作。
- 使用https://us-west-2.console.aws.amazon.com/console进入 AWS 管理控制台。
- 在 AWS 管理控制台上,您可以选择“启动虚拟机”在这里,我们试图设置一台机器,在那里我们将部署我们的 FastAPI API。
- 第一步,您需要为机器选择 AMI 模板。我选的是 Ubuntu 以来的 18.04 Ubuntu 服务器。
- 在第二步中,我选择了
t2.xlarge
机器,它有 4 个 CPU 和 16GB RAM,而不是空闲层,因为我想使用对象检测模型,并且需要一些资源。
- 继续按下一步,直到你到达“6。配置安全组”选项卡。这是最关键的一步。您需要添加一个类型为“HTTP”且端口范围为 80 的规则。
- 您可以单击“查看并启动”,最后单击“启动”按钮来启动实例。单击启动后,您可能需要创建一个新的密钥对。这里我创建了一个名为
fastapi
的新密钥对,并使用“Download Key Pair”按钮下载它。请妥善保管此密钥,因为每次您需要登录到此特定机器时都会用到它。下载密钥对后,单击“启动实例”
- 现在,您可以转到您的实例,查看您的实例是否已经启动。提示:查看实例状态;它应该显示“运行”
- 另外,这里要注意的是公共 DNS(IPv4)地址和 IPv4 公共 IP。我们将需要它来连接到这台机器。对我来说,它们是:
Public DNS (IPv4): ec2-18-237-28-174.us-west-2.compute.amazonaws.comIPv4 Public IP: 18.237.28.174
- 一旦你在文件夹中运行了下面的命令,你就保存了
fastapi.pem
文件。如果文件被命名为fastapi.txt
,你可能需要将其重命名为fastapi.pem.
# run fist command if fastapi.txt gets downloaded.
# mv fastapi.txt fastapi.pemchmod 400 fastapi.pem
ssh -i "fastapi.pem" ubuntu@<Your Public DNS(IPv4) Address>
现在我们已经启动并运行了 Amazon 实例。我们可以在这里继续讨论帖子的真实部分。
2.创建用于对象检测的 FastAPI API
在我们部署 API 之前,我们需要有一个 API,对吗?在我最近的一篇文章中,我写了一个简单的教程来理解 FastAPI 和 API 基础。如果你想了解 FastAPI 的基础知识,请阅读这篇文章。
因此,这里我将尝试创建一个图像检测 API。至于如何把图像数据传递给 API?想法是——除了字符串,什么是图像? 一个图像只是由字节组成,我们可以把这些字节编码成一个字符串。我们将使用 base64 字符串表示法,这是一种将二进制数据转换为 ASCII 字符的流行方法。并且,我们将传递这个字符串表示来给我们的 API 一个图像。
A.一些图像基础知识:什么是图像,但一个字符串?
所以,让我们先看看如何将图像转换成字符串。我们使用‘rb’
标志从图像文件中读取二进制数据,并使用base64.b64encode
函数将其转换为 base64 编码的数据表示。然后,我们使用decode
到utf-8
函数将基本编码数据转换成人类可读的字符。如果现在还没什么意义,也不用担心。 只要明白任何数据都是二进制的,我们可以使用一系列步骤将二进制数据转换为其字符串表示。
举个简单的例子,如果我有一个像下面这样的简单图像,我们可以用下面的方法把它转换成一个字符串:
狗 _ 带 _ 球. jpg
import base64with open("sample_images/dog_with_ball.jpg", "rb") as image_file:
base64str = base64.b64encode(image_file.read()).decode("utf-8")
我们可以得到任何图像的字符串表示
在我的笔记本电脑上,这里有一个名为dog_with_ball.png
的文件的字符串表示。
很好,我们现在有了图像的字符串表示。并且,我们可以将这个字符串表示发送给我们的 FastAPI。但是我们还需要有一种方法从图像的字符串表示中读回图像。毕竟,我们使用 PyTorch 和任何其他包的图像检测 API 需要有一个可以预测的图像对象,这些方法不能在字符串上工作。
这里有一种从图像的 base64 字符串创建 PIL 图像的方法。大多数情况下,我们只是以同样的顺序做相反的步骤。我们使用.encode.
在‘utf-8’
中编码,然后使用base64.b64decode
解码成字节。我们使用这些字节通过io.BytesIO
创建一个 bytes 对象,并使用Image.open
将这个 bytes IO 对象作为 PIL 图像打开,这可以很容易地用作我的 PyTorch 预测代码的输入。 再简单一点,它只是将 ***base64***
图像串转换为实际图像的一种方式。
import base64
import io
from PIL import Imagedef base64str_to_PILImage(base64str):
base64_img_bytes = base64str.encode('utf-8')
base64bytes = base64.b64decode(base64_img_bytes)
bytesObj = io.BytesIO(base64bytes)
img = Image.open(bytesObj)
return img
那么这个功能起作用吗?我们自己看吧。我们可以只使用字符串来获取图像。
我们的快乐狗狗又回来了。比绳子好看。
B.编写实际的 FastAPI 代码
因此,现在我们知道我们的 API 可以从我们的用户那里获得一个字符串形式的图像,让我们创建一个对象检测 API,它使用这个字符串形式的图像,并输出带有对象类的对象的边界框。
在这里,我将使用来自torchvision.models
的 Pytorch 预训练fasterrcnn_resnet50_fpn
检测模型进行对象检测,该模型是在 COCO 数据集上训练的,以保持代码简单,但可以使用任何模型。如果你想用 Pytorch 训练你的自定义图像分类或者图像检测模型,可以看看这些帖子。
下面是 FastAPI 的完整代码。虽然它看起来很长,但我们已经知道了所有的部分。在这段代码中,我们主要执行以下步骤:
- 使用 FastAPI()构造函数创建我们的 fast API 应用程序。
- 加载我们的模型和它被训练的类。我从 PyTorch 文档中获得了课程列表。
- 我们还定义了一个新的类
Input
,它使用一个名为pydantic
的库来验证我们将从 API 最终用户那里获得的输入数据类型。在这里,终端用户给出base64str
和一些用于目标检测预测的分数threshold
。 - 我们添加了一个名为
base64str_to_PILImage
的函数,正如它的名字一样。 - 我们编写了一个名为
get_predictionbase64
的预测函数,它使用图像的 base64 字符串表示和一个阈值作为输入,返回边界框和类的字典。我们还在这个函数的顶部添加了[@app](http://twitter.com/app).put(“/predict”)
来定义我们的端点。如果你需要理解 put 和 endpoint,请参考我之前在 FastAPI 上发表的文章。
C.先本地后全局:在本地测试 FastAPI 代码
在我们转向 AWS 之前,让我们检查一下代码是否在我们的本地机器上工作。我们可以使用以下命令在笔记本电脑上启动 API:
uvicorn fastapiapp:app --reload
以上意味着您的 API 现在正在您的本地服务器上运行,--reload
标志表示当您更改fastapiapp.py
文件时,API 会自动更新。这在开发和测试时非常有用,但是当您将 API 投入生产时,您应该删除这个--reload
标志。
您应该会看到类似这样的内容:
现在,您可以尝试使用请求模块来访问这个 API 并查看它是否工作:
import requests,jsonpayload = json.dumps({
"base64str": base64str,
"threshold": 0.5
})response = requests.put("[http://127.0.0.1:8000/predict](http://127.0.0.1:8000/predict)",data = payload)
data_dict = response.json()
所以我们使用 API 得到我们的结果。这个图像包含一只狗和一个运动球。我们也有边界框的角 1 ( x1,y1)
和角 2 ( x2,y2
)坐标。
D.让我们想象一下
虽然并非绝对必要,但我们可以在 Jupyter 笔记本中想象结果:
以下是输出:
这里你会注意到,我从本地文件系统中获取了图像,这可以被认为是欺骗,因为我们不想保存用户通过 web UI 发送给我们的每个文件。我们应该能够使用相同的base64string
对象,我们也必须创建这个图像。对吗?
别担心,我们也能做到。还记得我们的base64str_to_PILImage
功能吗?我们也可以用这个。
img = base64str_to_PILImage(base64str)
drawboundingbox(img, data_dict['boxes'], data_dict['classes'])
看起来很棒。我们有工作的 FastAPI,也有 amazon 实例。我们现在可以开始部署了。
3.在 Amazon ec2 上部署
到目前为止,我们已经创建了一个 AWS 实例,还创建了一个 FastAPI,它将图像的 base64 字符串表示作为输入,并返回边界框和相关的类。但是所有的 FastAPI 代码仍然驻留在我们的本地机器上。 我们怎么把它放到 ec2 服务器上?并在云上运行预测。
A.安装 Docker
我们将使用 docker 部署我们的应用程序,正如fastAPI
创建者自己所建议的那样。我会试着解释 docker 是如何工作的。下面的部分可能看起来令人生畏,但它只是一系列的命令和步骤。所以和我在一起。
我们可以从安装 docker 开始,使用:
sudo apt-get update
sudo apt install docker.io
然后,我们使用以下命令启动 docker 服务:
sudo service docker start
B.为 docker 创建文件夹结构
└── dockerfastapi
├── Dockerfile
├── app
│ └── main.py
└── requirements.txt
这里dockerfastapi
是我们项目的主文件夹。这是这个文件夹中的不同文件:
一、 **requirements.txt**
: Docker 需要一个文件,这个文件告诉它我们的 app 运行需要哪些所有的库。这里我列出了我在 Fastapi API 中使用的所有库。
numpy
opencv-python
matplotlib
torchvision
torch
fastapi
pydantic
二。 **Dockerfile**
: 第二个文件是 Dockerfile。
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7COPY ./app /app
COPY requirements.txt .
RUN pip --no-cache-dir install -r requirements.txt
Docker 是如何工作的?: 你可以跳过这一节,但这将有助于了解 docker 是如何工作的。
可以把dockerfile
想成类似于sh file,
的东西,它包含创建可以在容器中运行的 docker 映像的命令。人们可以把 docker 映像想象成一个安装了 Python 和 Python 库等所有东西的环境。容器是一个单元,它只是我们系统中一个使用 docker image
的孤立盒子。使用 docker 的好处是,我们可以创建多个 docker 映像,并在多个容器中使用它们。例如,一个图像可能包含 python36,而另一个图像可能包含 python37。我们可以在一台 Linux 服务器上生成多个容器。
我们的Dockerfile
包含了几样东西:
FROM
命令:这里第一行FROM
指定我们从tiangolo’s
(FastAPI creator) Docker 镜像开始。根据他的网站:“这个图像有一个“自动调整”机制,这样你就可以添加你的代码,自动获得同样的高性能。【不作牺牲】。我们所做的只是从一个镜像开始,它为我们安装了 python3.7 和,并自动为uvicorn
和gunicorn
ASGI 服务器添加了一些配置和一个用于 ASGI 服务器的start.sh
文件。对于喜欢冒险的人来说,特别是[commandset](https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker/blob/master/docker-images/python3.7.dockerfile)1
和[commandset2](https://github.com/tiangolo/uvicorn-gunicorn-docker/blob/master/docker-images/python3.7.dockerfile)
通过一种命令菊花链的方式被执行。COPY
命令:我们可以把 docker 镜像看作一个包含文件等的文件夹。在这里,我们将之前创建的app
文件夹和requirements.txt
文件复制到 docker 映像中。RUN
命令:我们运行 pip install 命令,使用 docker 映像上的requirements.txt
文件安装所有 python 依赖项。
三。main.py: 这个文件包含了我们之前创建的fastapiapp.py
代码。记住只保留文件名main.py
。
C.码头工人建造
我们已经得到了所需结构的所有文件,但是我们还没有使用任何 docker 命令。我们首先需要使用 Dockerfile 构建一个包含所有依赖项的映像。
我们可以简单地通过以下方式做到这一点:
sudo docker build -t myimage .
这将从tiangolo’s
映像下载、复制和安装一些文件和库,并创建一个名为myimage.
的映像。这个myimage
有 python37 和一些由requirements.txt
文件指定的 python 包。
然后我们只需要启动一个运行这个图像的容器。我们可以通过以下方式做到这一点:
sudo docker run -d --name mycontainer -p 80:80 myimage
这将创建一个名为mycontainer
的容器,它运行我们的 docker 映像myimage
。部分80:80
将我们的 docker 容器端口 80 连接到我们的 Linux 机器端口 80。
事实上就是这样。此时,您应该能够在浏览器中打开下面的 URL。
# <IPV4 public IP>/docs
URL: 18.237.28.174/docs
我们可以通过编程来检查我们的应用程序,使用:
payload = json.dumps({
"base64str": base64str,
"threshold": 0.5
})response = requests.put("[http://18.237.28.174/predict](http://18.237.28.174/predict)",data = payload)
data_dict = response.json()
print(data_dict)
是的,我们的 API 终于部署好了。
D.故障排除,因为现实世界并不完美
来源:因为这就是真实世界部署的样子。
以上所述都是好的,如果你严格按照说明去做,它们会立即发挥作用,但是现实世界并不是这样的。在这个过程中,您肯定会遇到一些错误,并且需要调试您的代码。因此,为了帮助您,一些 docker 命令可能会派上用场:
- **日志:**当我们使用
sudo docker run
运行我们的容器时,我们没有得到很多信息,这是调试时的一个大问题。您可以使用下面的命令查看实时日志。如果您在这里看到一个错误,您将需要更改您的代码并重新构建映像。
sudo docker logs -f mycontainer
- 启动和停止 Docker: 有时候,重启 Docker 可能会有帮助。在这种情况下,您可以使用:
sudo service docker stop
sudo service docker start
- **列出图像和容器:**使用 docker,您将最终创建图像和容器,但是您将无法在工作目录中看到它们。您可以使用以下方式列出图像和容器:
sudo docker container ls
sudo docker image ls
- **删除未使用的 docker 图像或容器:**您可能需要删除一些图像或容器,因为它们会占用系统的大量空间。这是你如何做的。
# the prune command removes the unused containers and images
sudo docker system prune# delete a particular container
sudo docker rm mycontainer# remove myimage
sudo docker image rm myimage# remove all images
sudo docker image prune — all
- **检查 localhost:**Linux 服务器没有浏览器,但是我们仍然可以看到浏览器的输出,虽然有点难看:
curl localhost
- **开发时不需要一次又一次地重新加载映像:**对于开发来说,只需在我们的机器上更改代码内容并进行现场测试,而不必每次都构建映像,这是非常有用的。在这种情况下,在每次代码更改时自动运行带有实时自动重载的服务器也是很有用的。这里,我们使用 Linux 机器上的应用程序目录,在开发过程中,我们用开发选项
/start-reload.sh
替换缺省值(/start.sh
)。一切正常后,我们可以再次构建我们的映像,并在容器中运行它。
sudo docker run -d -p 80:80 -v $(pwd):/app myimage /start-reload.sh
如果这还不够,在这里添加一个包含有用的 docker 命令的 docker 备忘单:
4.带有用户界面的端到端应用
我们已经完成了 API 的创建,但是我们还可以使用 FastAPI 创建一个基于 UI 的应用程序。这不是你在生产环境中要做的事情(在生产环境中,你可能让开发人员使用 react、node.js 或 javascript 来制作应用程序),但这里主要是检查如何使用图像 API 的端到端流程。我将在本地而不是 ec2 服务器上托管这个准系统 Streamlit 应用程序,它将从托管在 ec2 上的 FastAPI API 获取边界框信息和类。
如果你需要了解更多关于 streamlit 是如何工作的,你可以看看这篇帖子。此外,如果您想将这个 streamlit 应用程序也部署到 ec2,这里还有一个教程。
下面是 ec2 上带有 UI 和 FastAPI API 的整个 app 的流程:
项目架构
在我们的 streamlit 应用中,我们需要解决的最重要的问题是:
如何使用 Streamlit 从用户处获取图像文件?
**答:使用文件上传器:**我们可以通过以下方式使用文件上传器:
bytesObj = st.file_uploader(“Choose an image file”)
下一个问题是,我们从 streamlit 文件上传者那里得到的这个 bytesObj 是什么?在 streamlit 中,我们将从file_uploader
中获得一个bytesIO
对象,我们需要将它转换为 base64str,用于我们的 FastAPI 应用程序输入。这可以通过以下方式实现:
def bytesioObj_to_base64str(bytesObj):
return base64.b64encode(bytesObj.read()).decode("utf-8")base64str = bytesioObj_to_base64str(bytesObj)
B .使用 URL: 我们也可以使用text_input
从用户那里获得一个图片 URL。
url = st.text_input(‘Enter URL’)
然后,我们可以使用请求模块和 base64 编码和utf-8
解码从 URL 获取 base64 字符串格式的图像:
def ImgURL_to_base64str(url):
return base64.b64encode(requests.get(url).content).decode("utf-8")base64str = ImgURL_to_base64str(url)
这是我们的 Streamlit 应用程序的完整代码。你已经看到了这篇文章中的大部分代码。
我们可以使用以下方式在本地运行这个 streamlit 应用程序:
streamlit run streamlitapp.py
我们可以看到在我们的localhost:8501.
上运行的应用程序可以很好地处理用户上传的图像以及基于 URL 的图像。这里有一张猫的图片,也给你们中的一些猫爱好者。
我们使用所有组件— Streamlit、FastAPI 和 Docker 的最终结果。
原来如此。我们在这里创建了一个完整的工作流,通过 FastAPI 在 ec2 上部署图像检测模型,并在 Streamlit 中利用这些结果。我希望这能帮助您解决在生产中部署模型的问题。你可以在我的 GitHub 库找到这篇文章的代码以及我所有的文章。
让我知道你是否喜欢这篇文章,以及你是否愿意在你的日常部署需求中包含 Docker 或 FastAPI 或 Streamlit。我也希望在 Docker 上创建一个更详细的帖子,请关注我,关注我的写作。详情如下。
继续学习
如果你想了解更多关于建立机器学习模型并将其投入生产的知识,AWS 上的这个课程可以满足你的要求。
谢谢你的阅读。将来我也会写更多初学者友好的帖子。在 Medium 上关注我或者订阅我的博客来了解他们。一如既往,我欢迎反馈和建设性的批评,可以通过 Twitter @mlwhiz 联系
此外,一个小小的免责声明——这篇文章中可能会有一些相关资源的附属链接,因为分享知识从来都不是一个坏主意。
部署策略
如何选择正确的策略来满足您的应用需求
每个应用程序的部署都有一个时间点。我们可以将这个活动委托给一个自动化的任务或者手动完成,但是我们需要在幕后有一个部署策略。我读过许多关于部署的指南,其中大部分都很好地解释了如何实现给定的解决方案。与现实世界的缺失环节是将实施与需求相匹配。简而言之,在应用解决方案之前,您必须了解您的需求。这就是为什么我想把重点放在场景和策略上。
foto diFree-PhotosdaPixabay
为什么我们需要权衡
您将在下一段中发现,每种部署策略都满足不同的需求。识别您的用例对于选择正确的工具是至关重要的。每个解决方案都必须与应用程序要求兼容,这也包括列表中的预算变量。
你是对的;它带有权衡威胁的味道。但事实就是如此。你不能用火箭筒射杀一只苍蝇。
询问您的客户是否更喜欢不会使其服务离线的部署策略。我觉得很容易猜到它的答案。
如果可能的话,我们需要在部署过程中保持服务的活力。对于一个用户很少的简单应用程序来说,这看起来有点大材小用,但是用同样的努力实现零停机 DevOps 过程通常是可能的。因此,零停机时间可能是我们应该始终考虑的因素。
在许多情况下,我们需要同时维护两个版本(旧的和新的,有和没有新的特性,等等。).如果您考虑您的自动化部署服务,这个解决方案相对容易实现,可能需要一个基础设施创建阶段。重要的是成本。运行同一个应用程序的两个实例通常是链接运行两个简单的虚拟机。对于一个庞大的系统来说,运行一个应用程序的两个并行版本所需的基础设施翻倍的成本可能会令人望而却步。因此,保持多个系统同时可用可能是一个问题。
我可以继续举其他例子,但问题很简单。我们需要根据应用程序的实际需求来调整我们的解决方案。
过于简化会导致某些东西今天有效,明天就会失效。过于远视会让你睡得很香,但可能会影响你的预算。
部署策略
再创造
旧版本关闭;然后,新的被激活。当您有一个简单的服务器,并且您更新您的网站时,这个过程就会发生。
我和❤️一起做的
优点:
- 易于实施
缺点:
- 需要很短的停机时间。
- 在某些错误的情况下,开关是硬的。
倾斜的
部署新版本并逐步交付给用户。当您发布一个新的应用程序,并且希望调整性能或某些关键特性时,建议使用这个解决方案。如果你打算在大规模发布之前收集用户的反馈并调整你的应用程序,这也是一个不错的选择。
我和❤️一起做的
优点:
- 进步分子
缺点:
- 达到完全部署需要很长时间
- 昂贵(如果您将此解决方案应用于现有应用程序,您将有两个运行环境)
蓝色\绿色
新版本与另一个版本一起部署;然后,流量被切换到新的服务器。只有当新版本上线并经过测试时,切换才会发生,因此操作是即时且安全的。
我和❤️一起做的
优点:
- 安全的
- 无停机时间
缺点:
- 难以实现
- 暂时需要两个运行环境(昂贵)
- 许多 web 服务器在内部实现了蓝/绿机制,因此您可以开箱即用。
A/B 测试
两个版本,新的和旧的,一起工作。一些目标用户登陆新网站,这样你就可以得到反馈
我和❤️一起做的
优点:
- 您可以比较这两个平台并获得反馈
缺点:
- 昂贵的
- 很难在完整的应用程序上实现,因为您需要在同一个数据库上运行两个应用程序
阴影
新版本的应用程序通过旧平台接收流量,并向呼叫者提供复古兼容的响应。
我和❤️一起做的
优点:
- 确保追溯兼容性
- 对用户透明
缺点:
- 很难在完整的 web 应用程序上实现
- 给你的限制,以符合旧的应用程序永远。
选择正确的策略。
嗯,这取决于需求和预算。这第一句话是无可挑剔的,但完全没有用😅。因此,我将尝试为每种情况绘制正确的解决方案,以便您可以选择正确的解决方案。
新的应用土地推向市场
当一个新的应用程序投入生产时,没有什么可破坏的,所以一切都更容易。在上线之前,您可以使用重新创建或绿/蓝方法激活新的应用程序。这个提议唯一的遗憾是,你将有一个完整的开关。在发布日,所有用户将一起进入您的 web 应用程序。在没有任何 bug、分析漏洞或性能问题的理想世界中,这不是问题。在现实世界中,交钥匙总是一个赌注,即使您已经做了最好的分析,您测试了所有的东西两次,并且运行了性能测试。我的建议是尽可能进行倾斜部署。从有限数量的用户开始,让您收集反馈并解决问题,而不会出现紧急情况。
现有应用程序的更新
当一个应用程序运行时,我觉得很难逐步部署一个新版本或者在应用程序级别进行 A/B 测试。最大的问题是,您需要运行两个不同的应用程序,费用加倍,并且两个应用程序必须共享同一个数据库。但是还有一个实际的限制。谁了解用户?谁来决定哪个客户有资格购买 A 或 B 版本?答案很简单:商业逻辑。因此,更容易管理的地方是应用程序内部,也就是业务逻辑所在的地方。要解决不同功能之间的平衡需求,更简单的解决方案是在应用程序级别进行操作。您可以简单地部署一个具有两个用户可以激活的特性的应用程序。在微服务时代,添加或删除部件并应用一些业务逻辑是相对容易的。为什么不呢?
一旦您能够管理 A/B 测试或应用程序内部新特性的激活,您将需要一个不会破坏任何东西的部署。蓝/绿部署就可以了。
您想要替换一个遗留的 web 服务。
当您有成千上万用户使用的遗留 API 时,替换它总是一团糟。问题可能来自使用 IP 地址而不是 DNS 的用户,或者防火墙问题。任何微小的改变都可能引发问题,所以我们要超级保守。影子版本部署是正确的解决方案。嘲笑所有的回应,打电话的人可以继续像往常一样,而你启动了新的系统。然后,您可以要求用户切换并监控旧 API 的使用,但不要着急。最后可以关闭老界面,不用担心会切走一部分用户。最大的问题是维护两个不同系统并使它们保持一致的成本,几乎是在与用户的界面上(如果您在真实服务上做了一些更改,模拟需要保持与初始定义的一致性)。此外,您必须在模拟接口上支持旧的 API 接口的事实可能会在某种程度上限制您的能力。
您希望向现有产品添加新功能。
当您在现有项目上添加一个特性,并且希望在完全注册之前进行正确的测试时,A/B 或渐变部署会很有用。第一个让您比较两个应用程序对用户的影响,但是第二个逐渐引入了一个新特性。这两种方法都是提高用户参与度的有趣且有用的方法,但很难针对每个功能实现。主要问题是保持应用程序的两个并发版本一起运行。这意味着双倍的基础设施成本、双倍的维护成本,需要保持数据库与两个正在运行的应用程序兼容。对于所有这些问题,在大多数情况下,您可以在应用程序级别管理逐步部署。这种方法还有一个优点,就是您可以在用户范围内非常容易地为每个用户定义一个定制策略。
带什么回家
部署应用程序是 DevOps 的一个棘手的方面,必须考虑一些业务需求才能有效。像往常一样,有很多选择。很多都管用,但是你可以挑最适合你的。
被认为有害的深度?
我们需要深度图神经网络吗?
由迈克尔布朗斯坦 — 9 分钟阅读
深度学习的标志之一是使用数十层甚至数百层的神经网络。与之形成鲜明对比的是,图形深度学习中使用的大多数架构都很浅,只有少数几层。在这篇文章中,我提出了一个异端的问题:图神经网络架构中的深度带来任何优势吗?
刘易斯·恩古吉在 Unsplash 上拍摄的照片
有抱负的数据科学家的 5 个典型思维错误
由孙铁麟迈斯特 — 6 分钟读取
在过去的几年里,我和 500 多名有抱负的数据科学家一起工作过,我看到了他们容易犯的一些典型的思维错误。在这篇文章中,我想分享其中的五个。
基于歌词的歌曲推荐,带有 Doc2Vec 嵌入和 Spotify 的 API
由亚当·里维斯曼 — 5 分钟阅读
单词嵌入对于自然语言处理来说是一个非常有用的工具。它们通常作为神经网络的参数被学习,并允许我们将单词映射到数字。更具体地说,它们允许我们将单词映射到高维向量。
照片由 Riho Kroll 在 Unsplash 上拍摄
方差注入式思维
通过饶彤彤 — 8 分钟读取
历史看起来是确定的,而且经常被这样解释。本世纪初的科技泡沫显然是一个泡沫,正如所有泡沫都会发生的那样,它最终破裂了。20 世纪 80 年代的日本房地产,2008 年的美国房地产,20000 美元的比特币,杂草股等等也是如此。在崩盘后的几年里,历史学家关注的是风险有多明显,最终的内爆有多不可避免。
Kubernetes 上的气流之旅
由马塞洛·拉韦略·罗西 — 12 分钟读完
我对 Apache Airflow 的拙见:基本上,如果您有多个自动化任务要调度,并且您正在摆弄 cron 任务,即使它们的一些依赖项失败了,您也应该尝试一下。
深度估计:基础和直觉
在 Unsplash 上由 Shea Rouda 拍摄的照片
深度对于 3D 视觉至关重要
测量相对于相机的距离仍然很困难,但绝对是开启自动驾驶、3D 场景重建和 AR 等激动人心的应用的关键。在机器人技术中,深度是执行感知、导航和规划等多种任务的关键先决条件。
创建 3D 地图将是另一个有趣的应用,计算深度允许我们将从多个视图捕获的图像投影到 3D 中。然后,对所有的点进行配准和匹配,就可以完美地重构场景。
来源:3D 重建场景
需要解决的一些挑战性问题包括**对应匹配、**由于诸如纹理、遮挡、非朗伯表面等原因而变得困难、**解决不明确的解决方案、**其中许多 3D 场景实际上可以在图像平面上给出相同的图片,即预测的深度不是唯一的。
从相机获取距离信息因其相对低廉的生产成本和密集的表现形式而极具吸引力。将这项技术带给大众就像拥有一台现成的照相机一样简单。目前,检索深度的最佳替代方法是使用激光雷达等主动测距传感器。它们是天然高保真传感器,提供高度精确的深度信息。
由于各种原因,如遮挡、场景中的动态对象和不完美的立体对应,在深度估计方面进行工作,特别是在自主车辆的应用中,确实具有挑战性。对于立体匹配算法来说,反光、透明、镜面是最大的敌人。例如,汽车的挡风玻璃通常会降低匹配性能,从而降低估计性能。因此,大多数公司仍然依赖激光雷达来可靠地提取距离。然而,自动驾驶汽车感知堆栈的当前趋势是转向传感器融合,因为每个传感器在其提取的特征方面都有其优势。尽管如此,自深度学习出现以来,该领域已经获得了很大的牵引力并取得了显著的成果。许多研究致力于解决这些问题。
在计算机视觉中,深度是从两种流行的方法中提取的。即,来自单目图像(静态或连续)的深度或来自立体图像的深度。这篇文章将重点给读者一个深度估计的背景和与之相关的问题。需要对相机射影几何有充分的理解。
通过阅读这篇文章,我希望你能从总体上对深度知觉有一个直观的理解。此外,概述了深度估计研究的趋势和方向。然后我们将讨论一些(许多)相关的问题。
各种深度估计算法将在后续帖子中详细阐述,以免过多的细节淹没您!
我们如何看待这个世界
图二。投射到视网膜上(左)。投影到像平面(右)
让我们从我们人类一般如何感知深度开始。这将给我们一些关于深度估计的有价值的见解,因为这些方法中的许多是从我们的人类视觉系统中得到的。机器视觉和人类视觉在图像形成方式上有相似之处(图 2)。从理论上讲,当来自光源的光线击中表面时,它会反射并射向我们视网膜的背面,投射出去,我们的眼睛将它们作为 2D [1]进行处理,就像图像在图像平面上形成一样。
那么,当投影场景在 2D 时,我们如何在 3D 中测量距离和理解我们的环境呢?例如,假设有人要给你一拳,你本能地知道你什么时候会被击中,当他/她的拳头靠得太近时,你会躲开它!或者当你开车时,你可以通过某种方式判断何时踩油门或踩刹车,以便与其他司机和行人保持安全距离。
这里的工作机制是,我们的大脑开始通过识别模式来推理传入的视觉信号,如大小、纹理和场景的运动,称为深度线索。没有关于图像的距离信息,但是我们可以毫不费力地解释和恢复深度信息。我们感知到场景的哪一面离我们近,哪一面离我们远。此外,这些线索让我们能够将平面图像上的物体和表面视为 3D [1]。
如何破坏深度(不是人类/计算机视觉)
只是为了强调一个有趣的事实,解释这些深度线索始于场景如何在人类和摄像机视觉中被投射到透视图。另一方面,正视图或侧视图的正交投影会破坏所有深度信息。
考虑图 3,观察者可以分辨出房子的哪个方面离他/她更近,如左图所示。然而,完全不可能从正确的图像中区分相对距离。甚至背景也可能和房子在同一平面上。
利用线索判断深度
基本上有 4 类深度线索:静态单眼、运动深度、双目和生理线索[2]。我们下意识地利用这些信号来非常好地感知深度。
图像深度线索
我们从单一静止图像中感知深度的能力取决于场景中事物的空间排列。下面,我总结了一些提示,让我们能够推理不同物体的距离。从你与地球母亲的日常互动中,你可能已经感觉很自然了。希望不要花太多心思去计算各种线索。
由蜜桃红·坎普斯·菲利佩在 Unsplash 上拍摄的照片
加州大学伯克利分校进行了一项有趣的研究,他们通过实验表明,当视界可见时,我们有一种压倒性的倾向来利用这一特性快速感知深度。当你看到上面的图片时,你会这样想吗?
来自运动的深度线索(运动视差)
图五。运动视差
你也不应该对此感到惊讶。作为一个观察者,当你在运动时,你周围的物体比远处的物体经过得快。某样东西出现得越远,它离开观察者的速度似乎就越慢。
来自立体视觉的深度线索(双目视差)
视网膜差异:又一个有趣的现象,让我们能够识别深度,这可以从一个简单的实验中直观地理解。
图六。来源
闭上一只眼睛,将食指放在面前,尽可能靠近你的脸。现在,反复关闭一个,打开另一个。注意到你的手指在动!你的左右眼观察到的视野差异被称为视网膜差异。现在伸出一臂长的手指,做同样的动作。你应该注意到手指位置的变化变得不那么明显了。这应该给你一些关于立体视觉如何工作的线索。
这种现象被称为****;由于对世界的两种不同视角而感知深度的能力。通过比较两只眼睛视网膜的图像,大脑计算距离。差距越大,事情离你越近。
计算机视觉中的深度估计
深度估计的目标是获得场景的空间结构的表示,恢复图像中物体的三维形状和外观。这也被称为逆问题[3],在这种情况下,我们试图在信息不足以完全确定解决方案的情况下恢复一些未知数。这意味着 2D 视图和 3D 视图之间的映射不是唯一的(图 12 ),我将在这一部分介绍经典的立体方法和深度学习方法。
那么机器实际上是如何感知深度的呢?我们能否以某种方式转移上面讨论的一些想法?具有令人印象深刻的结果的最早算法始于 90 年代使用立体视觉的深度估计。密集立体对应算法取得了很多进展[4] [5] [6]。研究人员能够利用几何学从数学上限制和复制立体视觉的概念,同时实时运行。本文对所有这些观点进行了总结[7]。
至于单目深度估计,它最近开始通过使用神经网络来学习直接提取深度的表示而受到欢迎[8]。其中通过基于梯度的方法隐含地学习深度线索。除此之外,在自我监督的深度估计方面已经有了很大的进步[9][10][11]。这是特别令人兴奋和开创性的!在这种方法中,模型被训练成通过优化代理信号来预测深度。训练过程中不需要地面真相标签。大多数研究要么利用几何线索,如多视图几何或核几何来学习深度。我们稍后会谈到这一点。
来自立体视觉的深度估计
图 7。极线几何(左)。校正后的图像(右)
使用立体相机解决深度的主要思想涉及到三角测量和立体匹配的概念。形式依赖于良好的校准和校正来约束问题,以便它可以在被称为核平面的 2D 平面上建模,这极大地将后者简化为沿着核线**(图 7) 的线搜索。关于极线几何的更多技术细节将在以后的帖子中讨论。**
类似于双目视差,一旦我们能够匹配两个视图之间的像素对应,下一个任务是获得编码差异的表示。这种表示被称为视差,d.** 为了从视差中获得深度,可以从相似的三角形中计算出公式(图 8)**
图 8。立体几何
这些步骤如下
- 从特征描述符中识别相似点。
- 使用匹配成本函数匹配特征对应。
- 使用极线几何,找到并匹配一个画框与另一个画框的对应关系。匹配成本函数[6]用于测量像素相异度
- 根据已知的对应关系
d = x1 — x2
计算视差,如图 8 所示。 - 根据已知视差计算深度
z = (f*b)/d
图九。来自 Kitti 的差异表示
深度学习时代
深度学习擅长高级感知和认知任务,如识别、检测和场景理解。深度知觉属于这一类,同样应该是一种自然的前进方式。目前有 3 个广泛的框架来学习深度:
监督学习:直接从单目图像估计深度的开创性工作始于 sa xena【8】。他们通过监督学习,通过最小化回归损失,学会了直接从 2D 图像中的单眼线索回归深度。从那以后,许多不同的方法被提出来,通过提出新的体系结构或损失函数来改善表征学习
****使用 SFM 框架的自监督深度估计:该方法将问题框架化为学习从视频序列生成新视图。神经网络的任务是通过在不同的时间步长I_t-1, I_t+1
拍摄图像并应用从姿态网络学习的变换来执行图像扭曲,从源视图生成目标视图I_t
。通过使用空间变换网络[14]以可区分的方式将扭曲视图合成视为监督,训练成为可能。在推断时间,深度 CNN 将从单视图 RGB 图像预测深度(图 10)。我建议您阅读这篇文章以了解更多信息。请注意,这种方法确实有一些缺点,如无法确定比例和模拟下一节中描述的移动对象。
图 10。无监督单目深度估计
使用立体图的自我监督单目深度估计:又一个有趣的方法。这里(图 11),代替将图像序列作为输入,该模型将仅从左侧 RGB 预测视差d_l, d_r
,I_l
。与上述方法类似,空间转换器网络使用视差扭曲 RGB 图像对I_l, I_r
。回想一下x2 = x1 — d
。因此可以合成成对视图,并且使用重建视图I_pred_l, I_pred_r
和目标视图I_l, I_r
之间的重建损失来监督训练。
为了使这种方法有效,假设基线必须是水平的和已知的。必须对图像对进行校正,以便通过视差进行准确的变换。从而计算结果d = x1 — x2
如图 8 所示。
图 11。使用立体的自我监督单目深度估计
CNN 深度线索和偏差学习
理解和破译黑盒一直是可解释机器学习中正在进行的研究。在深度估计的背景下,一些工作已经开始研究神经网络依赖于什么样的深度线索或者从特定数据集学习的归纳偏差。
在汤姆等人的开创性工作中,ICCV 2019 ,他们进行了几个简单的测试,通过实验找到了估计深度和场景结构之间的关系。请注意,这是在 Kitti 数据集上完成的,Kitti 数据集是一个室外道路场景,具有固定的相机位置,并且具有某种程度上可见的消失点和地平线。
物体相对于地面接触点的位置提供了强大的上下文信息:对于道路上的物体,通过增加汽车在垂直方向上的位置。我们看到,当模型离地面较远时,它不能很好地估计深度。
****
源:物体下面的阴影作为深度估计的强特征
形状不重要,但阴影重要:在另一个实验中,通过放置一个带有人工投射阴影的任意对象,即使在训练期间无法获得深度,模型也可以合理地估计深度。
Rene 等人完成了有趣的工作,他们在由室内和室外场景组成的相对较大规模的数据集上训练了一个深度模型。从观察来看,有一个自然的偏差,图像的下半部分总是靠近相机。这可以被视为下图右上角示例中的一种故障模式。此外,深度模型倾向于预测内容,而不是在左下角的情况下识别为镜子中的反射。在论文中还可以发现许多其他有趣的发现。
进行的研究仍然相当有限。要提供更多的结论性发现,还需要做大量的工作。
为什么测量深度这么难?
最后,让我们试着理解深度估计的一些基本问题。主要原因在于 3D 视图到 2D 图像的投影丢失了深度信息。当有运动和移动物体时,另一个问题就根深蒂固了。我们将在本节中详细介绍它们。
深度估计是不适定的
通常在进行单目深度估计的研究时,许多作者会提到从单幅 RGB 图像估计深度的问题是不适定的逆问题。这意味着世界上观察到的许多 3D 场景确实可以对应于同一个 2D 平面(图 11 和 12)。
图 12。来源
不适定:单目深度估计的尺度模糊
回想一下,调整焦距将按比例缩放图像平面上的点。现在,假设我们以某个因子k
缩放整个场景 X,同时以因子1/k
缩放相机矩阵 P,图像中场景点的投影保持完全相同
x = PX = (1/k)P * (kX) = x
也就是说,我们永远无法仅从图像中恢复实际场景的精确比例!
请注意,单目基础技术存在此问题,因为可以通过已知基线恢复立体装备的比例。
不适定:投影模糊
假设我们对场景进行几何变换,有可能变换后,这些点会映射到平面上的同一个位置。又一次给我们留下了同样的困难。见下图
降低匹配的属性
对于需要三角测量的基于立体或多视角的深度估计,通常涉及 检测-描述-匹配 的流水线。当场景是从非常不同的视点拍摄的或者图像之间的光照变化很大时,匹配变得非常困难。下图给出了一个极端情况,其中描述符无法区分特征。这些有问题的案例包括:
- 无纹理区域:许多像素将具有相同的像素强度
- 反射表面
- 重复模式
- 遮挡:对象在一个视图中被遮挡,但在另一个视图中不被遮挡。
- 违反朗伯性质:朗伯曲面指的是无论从哪里看都具有相同亮度的曲面。当图像从两个不同的视角显示同一场景时,由于非理想的漫反射,相应的亮度强度可能不相等。
一个非常困难的场景,有很大的视角差异和不同的人群。
移动物体违反了 SFM 方法的静态假设
场景中的动态对象进一步使估计过程复杂化。从运动中通过结构进行深度估计涉及移动的摄像机和连续的静态场景。这个假设必须适用于匹配和对齐像素。当场景中有移动的对象时,这个假设就不成立了。为此,许多研究人员研究了几种方法来模拟场景中的移动对象,包括使用光流[12]合并速度信息,或使用实例分割遮罩来模拟对象从一帧到另一帧的运动[13]。
下一步是什么
我希望你已经从这篇介绍性的文章中获得了一些关于深度估计的有价值的见解,为什么它是一个具有挑战性但又极其重要的任务,以及该技术的当前状态是什么。我坚信深度可以用相机和视觉来解决。我对此很乐观。因为我们自己仅仅依靠单眼或双眼视觉来与我们的环境互动。
感谢您阅读这篇文章。希望它给你一些好的见解!点击查看更多关于计算机视觉和机器学习的帖子。欢迎留下任何反馈:)
你对你预测的深度有把握吗?
towardsdatascience.com](/uncertainty-in-depth-estimation-c3f04f44f9) [## 自我监督的深度估计:打破观念
在这篇文章中,我想通过自我监督学习来分解各种各样的深度估计的想法。对于…
towardsdatascience.com](/self-supervised-depth-estimation-breaking-down-the-ideas-f212e4f05ffa) [## 逆投影变换
深度和反向投影
towardsdatascience.com](/inverse-projection-transformation-c866ccedef1c)
参考
[1] 视觉科学导论,戴维·l·安德森
[2]双目和 3d 显示器的视觉舒适性,Frank L. Kooi,Alexander Toet,在 SPIE 国际光学工程学会会议录25(2):99–108,2004 年 8 月
[3]计算机视觉:算法和应用,Richard Szeliski
[4]m .奥托米和 t .卡纳德(1993 年)。多基线立体声。 IEEE 模式分析与机器智能汇刊,15(4):353–363。
[5]博伊科夫、维克斯列尔和扎比赫(1998 年)。早期视觉的可变窗口方法。 IEEE 模式分析与机器智能汇刊,20(12):1283–1294。
[6]伯奇菲尔德和托马西(1999 年)。像素到像素立体的深度不连续性。《国际计算机视觉杂志》,35(3):269–293。
[7]Scharstein d .和 Szeliski r .(2002 年)。稠密两帧立体对应算法的分类和评价。国际计算机视觉杂志,47(1):7–42。
[8] D. Eigen、C. Puhrsch 和 R. Fergus。使用多尺度深度网络从单幅图像预测深度图。NIPS,2014。
[9] R .加尔格、g .卡内罗和 I .里德。用于单视图深度估计的无监督 cnn:几何学拯救。ECCV,2016。
[10] T .周、m .布朗、n .斯奈夫利和 d .洛来自视频的深度和自我运动的无监督学习。CVPR,2017。
[11]戈达尔、奥达和布罗斯托。具有左右一致性的无监督单目深度估计。CVPR,2017。
[12] Z. Yang,P. Wang,Y. Wang,W. Xu 和 R. Nevatia .每一个像素都很重要:具有整体 3d 运动理解的无监督几何学习。arxiv.org/pdf/1806.10556, 2018。
[13] R. Mahjourian、M. Wicke 和 A. Angelova。使用 3d 几何约束从单目视频无监督学习深度和自我运动。IEEE 计算机视觉和模式识别会议论文集,第 5667-5675 页,2018 年。
[14]贾德尔伯格先生、西蒙扬先生、齐塞尔曼先生和卡武克库奥卢先生。空间变压器网络。在日本,2015 年。**
费希尔信息的推导
一些理论,一些例子,和一些见解
背景和动机
Fisher 信息是数理统计中的一个重要量,在极大似然估计(MLE)的渐近理论和 cramér–Rao 下界的规范中起着重要作用。
让我们看看费希尔信息的定义:
上面的描述似乎很公平。费雪信息是分数的方差。简单,容易,太棒了!
教科书经常陈述(有时没有证明)在正则条件下,以下三个量都等于费雪信息:
当我在大学第一次遇到这个材料时,我不清楚这三个量是如何相等的?在 Medium 上快速搜索发现了关于这个话题的大量报道。我觉得下面的证明值得了解。这些证明中使用的一些技术在概率论和数理统计的其他地方也是有用的。所以让我们开始吧。
1)费希尔信息=得分函数的二阶矩
2)费希尔信息=得分函数的梯度的负期望值
示例:伯努利随机变量的费希尔信息,以及与方差的关系
利用我们在上面学到的知识,我们来做一个快速练习。
最后的想法
希望以上有见地。正如我在以前的一些文章中提到的,我认为没有足够的人花时间去做这些类型的练习。对我来说,这种基于理论的洞察力让我在实践中更容易使用方法。我个人的目标是鼓励该领域的其他人采取类似的方法。我打算在未来写一些基础作品,所以请随时在【LinkedIn】和 上与我联系,并在 Medium 上关注我的更新!
从第一原理导出卷积
深度学习的基础
你有没有想过卷积有什么特别之处?在这篇文章中,我从第一原理推导出卷积,并表明它自然地出现在平移对称中。
某些原则的内涵为某些事实的内涵提供了便利。(克劳德·阿德里安·赫尔维蒂乌斯)
D 在我的本科学习期间,我在以色列理工学院学习电子工程,我总是对卷积这样一个重要的概念突然出现感到震惊。这个看似武断的定义像眼里的一粒沙子一样扰乱了信号处理世界原本美好的画面。让卷积从第一原理中产生,而不是让它成为假设,那该多好啊!正如我将在本文中展示的,这些首要原则是平移不变性或对称性的概念。
让我从基础信号处理课程中教授的公式开始,该公式定义了两个 n 维向量 x 和 w 的离散卷积:
这里,为了方便起见,我假设所有的索引都从零运行到n1,并且取模n;把向量想象成定义在圆上是很方便的。将上面的公式写成矩阵-向量乘法会产生一个非常特殊的矩阵,称为循环矩阵:
循环矩阵具有多对角线结构,每条对角线上的元素具有相同的值。它可以通过将向量 w [3】的移位(模 n )版本堆叠在一起而形成;为此,我使用符号 C ( w )来指代由向量 w 形成的循环矩阵。由于任何卷积x∫w都可以被等价地表示为循环矩阵C*(w)x的乘法,所以我将这两个术语互换使用。*
O 线性代数中我们最先学到的一点就是矩阵乘法是不可交换的,也就是一般情况下,AB≦BA。然而,循环矩阵是非常特殊的例外:
循环矩阵交换,
或者换句话说,C(w)C(u)=C(u)C(w)。对于任何循环矩阵,或者任何选择的 u 和 w 都是如此。等价地,我们可以说卷积是一个交换运算,x∫w=w∫x。
特别选择 w =[0,1,0…,0]会产生一个特殊的循环矩阵,将向量向右移动一个位置。这个矩阵被称为(右)移位算子【4】,用 S 表示。右移位运算符的转置是左移位运算符。显然,先左移再右移(反之亦然)没有任何作用,这意味着 S 是一个正交矩阵:
循环矩阵可以用它们的交换性来刻画。仅证明移位的交换性似乎就足够了(文献[5]中的引理 3.1):
矩阵是循环的当且仅当它随移位交换。
这个“当且仅当”语句的第一个方向导致了一个非常重要的性质,称为平移或移位等方差*【6】:卷积与移位的可交换性意味着,不管我们是先移位一个向量然后卷积它,还是先卷积然后移位,结果都是一样的。*
第二个方向允许我们将卷积定义为移位等变线性运算:为了移位交换,矩阵必须具有循环结构。这正是我们从一开始就渴望的,从平移对称的第一原理中产生卷积[7]。我们可以从移位等方差的要求出发,得出卷积的公式,作为满足它的唯一可能的线性运算,而不是像信号处理书籍中通常做的那样,给出卷积的公式并证明它的移位等方差性质。
作为移位和模糊操作可互换性的移位等变图解。
信号处理课程中教授的另一个重要事实是卷积和傅立叶变换之间的联系。在这里,傅里叶变换也是出人意料地出现,然后显示出它将卷积运算对角化,允许在频域中执行两个矢量的卷积,作为它们的傅里叶变换的逐元素乘积。从来没有人解释过这些正弦和余弦来自哪里,它们有什么特别之处。
为了追根究底,回想一下线性代数中的一个事实:
交换矩阵可联合对角化。
换句话说,两个满足 AB = BA 的矩阵将具有相同的特征向量(但可能是不同的特征值)[9]。由于所有循环矩阵都交换,我们可以选择其中的一个并计算其特征向量——上述定理确保这些也将是所有循环矩阵的特征向量。
方便选择换班操作符 S 。由于 S 是正交矩阵,我们期望它的特征向量是正交的[10]。一个简单的计算(见[5]中的第 4.1 节)得出如下结论
傅里叶变换将移位算子对角化。
我希望此时你已经有了今天的第二个“啊哈”时刻:这就是正弦和余弦的来源!它们是移位算子的特征向量;我将它们表示为矩阵φ的列。注意特征向量是复数,所以我们在转置φ时需要取复数共轭。乘以φ(从左起)称为傅立叶变换,乘以φ称为傅立叶逆变换。*
因为所有循环矩阵都是可联合对角化的,所以它们也可以通过傅立叶变换来对角化[11]。它们只是在特征值上有所不同。最后缺失的一点是意识到
C( w )的特征值是 w 的傅里叶变换。
我们现在可以把这个难题的所有部分放入一个被称为卷积定理的陈述中:卷积x∑w可以作为循环矩阵 C ( w )应用于原始坐标系中的 x (有时这被称为“空间域”卷积),或者通过首先计算的傅里叶变换,在傅里叶基(“谱域”)中计算
因为φ具有特殊的冗余结构,所以利用[快速傅立叶变换](https://en.wikipedia.org/wiki/Fast_Fourier_transform#:~:text=A%20fast%20Fourier%20transform%20(FFT,frequency%20domain%20and%20vice%20versa.) (FFT)算法,可以用𝒪( n log n 复杂度来计算乘积**φ x和φx**。*
为什么卷积的这种定义很重要,并且应该这样教?我在这里重复一下我在这篇文章开始时引用的赫尔维·提乌斯的话:“对某些原则的了解很容易弥补对某些事实的了解的不足”。在卷积的情况下,它从第一原理的推导允许容易地推广到其他领域。在下一篇文章中,我将展示如何在图上定义卷积,以产生图形深度学习架构的关键构建块。
[1] A. Dominquez-Torres,卷积的历史和起源对卷积运算的概念和符号的历史发展进行了有趣的探究。卷积积分第一次出现在 J. B. D’Alembert 的《世界体系的不同要点的研究》( 1754)中泰勒定理的推导中。这一优先权常常被错误地认为是 P.-S. de Laplace 的著作,载于《关于土地和功能的数字》(1773 年)。巴黎皇家科学院院士,学者与外国人 7:503–540,尽管该出版物不包含任何卷积的痕迹。拉普拉斯在他后来写于 1778 年并于 1781 年出版的概率回忆录中确实使用了卷积。早期的命名尝试包括 résultante (法语“合成”的意思,查尔斯·卡勒于 1899 年首次使用) composizione (意大利语“合成”的意思,维托·沃尔泰拉于 1910 年使用),以及 faltung (在德语中字面意思是“折叠”,古斯塔夫·多伊奇于 1923 年使用);后者主宰了 20 世纪初的德国文学。英文名 convolution 来源于拉丁语 con (“在一起”)和 volvere (“卷起”),是德语 faltung、的直译,俄语变体 свёртка 也是如此。这个英语术语的首次使用可以追溯到 1934 年 Aurel Friedrich Wintner 的论文;它后来被多奇(1937)和加德纳和巴恩斯(1942)的权威著作巩固在文学作品中。1910 年,沃尔泰拉首次使用了星形符号,尽管形式有所不同。珀西·约翰·丹尼尔使用了点符号。卷积的第一个现代符号为f∫g*,这是两者的组合,这是由于 Doetsch (1923)。*
【2】从技术上来说,我这里定义的是循环卷积*。*
[3]注意, C ( w )的行具有被转置的矢量 w ,导致出现在卷积公式中的反射,并将其与相关性的相关概念区分开来。注意边界条件(右上角和左下角的 C 的元素)。
[4]我交替使用术语运算符和矩阵*。*
[5] B. Bamieh,发现变换:关于循环矩阵、循环卷积和离散傅立叶变换的教程 (2018)。arXiv:1805.05533 提供了我在这篇文章中讨论的推导的细节。
[6]有些经常混淆不变性*(拉丁文中“不变”的意思)和等变(“同变”),很多信号处理书籍都把我这里讨论的性质称为“移位不变性”。一个功能是换挡同变iff(Sx)=Sf(x)。换句话说,我们是否先换档,然后再 f 都没有关系,反之亦然。不同的是,平移不变性是不受平移影响的性质:函数f(Sx)=f(x)是平移不变性。平移不变性是物理学中的一个基本概念(在这里它经常以“平移对称”的名义出现),表明物理定律不依赖于空间中的位置。在经典力学的变分公式中,通过 Noether 定理从位移不变性中得出动量守恒这样的基本定律。*
[7]等方差的概念是更一般的,可以用群论形式来扩展。T. Cohen 和 M. Welling, Group equivariant 卷积网络 (2016)中使用了这个框架。继续。ICML,将细胞神经网络卷积层的移位等方差扩展到更一般的操作,如旋转。假设 f : X→Y,其中 x 和 y 是一些不同的空间,对应的运算群𝒢和𝒢’分别定义在 x 和 y 的元素上,群等方差表示为f*(𝔤(x))=𝔤’(f(x))其中𝔤∈𝒢和𝔤’∈𝒢’.请注意,𝔤’不一定等于𝔤,因为输出空间 y 的结构和偶数维可以不同于输入空间 x 的结构和偶数维。本文中讨论的标准卷积是一种特殊情况,X=Y 是 n 维向量的空间,𝒢=𝒢’是平移组,𝔤=𝔤’是移位运算符。*
[8]由于我们处理的是有限维向量,所以术语“傅立叶变换”在这里指的是离散傅立叶变换 (DFT)。
[9]更准确地说,联合对角化意味着两个交换矩阵具有相同的特征空间*,因为在一般情况下,特征值可以具有非平凡的多重性。因为在我这里讨论的例子中,所有的特征值都是简单的,我们可以讨论一个公共的特征基。*
[10]但是,由于 S 是非对称的,所以它不具有实特征值(对称实矩阵具有实特征值)。 S 的特征值恰好是单位根的复根。
[11]当我说矩阵 C 被傅立叶变换“对角化”时,我的意思是矩阵**φ Cφ**是对角的。由于傅立叶变换是一个正交矩阵(φ*φ=I),从几何角度来说,它相当于坐标系统的一个变化,相当于一个 n 维旋转。在这个坐标系中, C 的动作变成了元素的乘积。*
[12]在信号处理中,通常在频域中设计滤波器,因此从不明确计算 w 的傅里叶变换。
关于图形深度学习的其他文章,请参见我的博客【迈向数据科学】* 订阅 到我的帖子,获取 中级会员 ,或者关注我的 推特 。*
从头开始推导反向传播方程(第一部分)
深入了解神经网络的训练方式
在这一系列简短的两篇文章中,我们将从头开始推导全连接(密集)层的三个著名反向传播方程:
以下所有解释都假设我们只向网络提供一个训练样本。如何将公式扩展到小批量将在这篇文章的最后解释。
正向传播
我们首先简要回顾一下单层的正向传播(以矩阵形式):
图层𝑙的输入是矢量(通常称为特征矢量):
方括号中的值(上标中)表示网络层。我们用变量 n 来表示一层中的节点数。定义图层𝑙行为的系数(权重和偏差)为:
中介价值:
被称为 的加权输入 是通过:
加权输入以元素方式馈入激活函数(也称为非线性):
要获得输出:
常见的激活功能有 ReLU、leaky ReLU、tanh、sigmoid、Swish 等。如上标所示,理论上每层可以具有不同的激活函数。
获得直觉
在不知道反向传播是什么的情况下推导反向传播方程是没什么用的。为此,我们首先将上一节定义的单个层链接成一个𝐿层网络。
(图片由作者提供)
训练神经网络的目标是提高其在给定任务上的性能,例如分类、回归。通过损失函数𝓛来评估性能,该损失函数在训练期间被添加为链的最后一个块。对于每个样本,损失函数将网络的输出与地面真实值𝒚进行比较,并输出单个实值(!idspnonenote)信号。)号。通常,较小的数字表示性能良好,而较大的数字表示性能不佳。
接下来,我们输入一个具体的样本到我们的网络中,例如
并通过各层连续向前传播。我们在图层𝑙停下来,看看计算出的加权输入,比如说:
然后,我们继续传播以获得损失函数值,例如𝓛 = 0.3247。
现在,我们回到𝑙层,并增加其第一个节点的值:
微小的价值:
同时保持所有其他节点不变。从𝑙层开始,我们向前传播改变后的加权输入:
通过网络并获得损失函数𝓛 = 0.3242 的微小变化值。
由于网络的分级性质,微小的推动δ导致了后面层𝑙 + 1、𝑙 + 2 等的所有激活。稍微更改它们的值,如下图所示:
绿色箭头表示增加,红色箭头表示减少(图片来自作者)
在我们设计的例子中,当我们增加𝑙.层中第一个节点的值时,损失函数的值减少了
δ𝓛=-0.0005 一般来说,对于一些节点,损失函数将减小,而对于其他节点,损失函数将增大。这完全取决于网络的权重和偏差。
上述两个三角形之比就是所谓的“斜率三角形”:
这个斜率三角形当然可以对所有节点和所有层进行计算。它是下列偏导数的近似值:
在文献中,这种偏导数通常被称为误差,我们将随后使用这个术语。误差正是从最后一层𝐿开始,通过网络反向传播的量。
通过使用第一反向传播方程,我们可以将层𝑙的误差向后传播一步到其前一层𝑙 -1:
重复应用该等式允许我们获得所有层的误差。
让我们简短地总结一下反向传播的机理:
训练神经网络的过程包括通过调整网络的权重和偏差来最小化损失函数。使用梯度下降或其变体来完成自适应。事实证明,偏置的梯度与误差完全匹配:
权重的梯度也可以通过外积直接从误差中获得:
扩展到多个样本
要计算小批量样品的重量和偏差梯度,我们只需独立执行上述步骤(!)对于小批量的所有样品,随后平均梯度:
其中,𝑚表示小批量样品的数量,圆括号中的数值表示样品编号。
请注意,在计算效率极高的反向传播“矢量化版本”中,针对单个样本独立计算误差。然而,计算是并行进行的。
链式法则
学校里教的基本链式法则允许我们计算嵌套函数的导数:
𝑔(.在哪里)和𝑓(.称之为“外层功能”)的“内在功能”。导数是:
也可以写成这样:
为了推导反向传播方程,我们需要对基本的链式法则稍加扩展。首先,我们扩展𝑔和𝑓函数,以接受多个变量。我们选择外部函数𝑔取三个实变量并输出一个实数:
因为𝑔现在接受三个变量,我们也需要三个不同的内部函数𝑓s,用于每个变量。假设每个𝑓s 接受两个实数变量并输出一个实数:
接下来,我们将内部函数插入外部函数,得到:
请注意,得到的函数只是两个(!idspnonenote)的函数。)实变量。现在,我们想获得下面的偏导数:
与上述基本链规则一样,我们首先获取外部函数𝑔相对于其第一个变量的偏导数,并将其乘以第一个内部函数的偏导数:
请注意,我们首先取外部函数𝑔的偏导数,就好像内部函数不存在一样。只是随后我们将内部函数插入到变量中,用垂直线表示。
然而,我们还没有完成,因为外部函数不仅仅依赖于一个变量。幸运的是,扩展链规则具有非常清晰的结构,因此我们接下来对第二个和第三个变量执行完全相同的步骤。随后,将所有步骤加在一起:
更一般地说,对于任意外函数和𝑛任意内函数:
链式法则是:
为了清楚起见,我们去掉了在具体值
同样的程序适用于其他变量的导数。
在本系列的第二部分中,我们将使用扩展链规则来推导反向传播方程。
降序:Python 中线性回归的一种尝试
线性回归快速指南以及如何使用梯度下降在 Python 中实现它。
克里斯·利维拉尼在 Unsplash 上的照片
这个项目的所有示例代码都可以在 这里 找到
在几乎每个人一生中的某个时刻,都有那么一刻,他们希望自己能预知未来。无论是通过使用臭名昭著的水晶球,还是通过在德洛里安跳跃到未来,未来仍然是一个不可能知道或预测的概念。
除非你从事数据科学。
使用原始数据并利用计算机科学和统计学创建模型的众多好处之一是,数据科学家可以根据现有数据和可观察到的趋势预测结果。
线性回归是一种技术,试图“通过将一个线性方程 T10 与观测数据 T11 拟合来模拟两个变量之间的关系”——耶鲁大学
这个线性方程给了我们一组数据点的最佳拟合线。使用这条线,既可以用肉眼看到,也可以作为其他预测模型的起点,我们能够以一定的准确度预测未来/增加的数据点。
随机散点图— Aleia Knight
取 1.0-10.0 之间的这组随机点,并将其绘制在散点图上。
有了这一组点,几乎立刻就能看出一个趋势。
正线性。
但是准确地知道直线的值,它的斜率和它的截距并不是仅仅通过查看原始图就可以得到的值。
最适合的系列——Aleia Knight
应用线性回归,我们可以看到最佳拟合的实际线。从这里,我们可以向前延伸线,并能够大致看到超过 10.0 和小于 1.0 的数据点可以预测到哪里。
足够简单,但数据很少会如此干净、简洁或直接。
如果我们使用像 Spotify 歌曲属性数据集这样的数据,并从中绘制一些数据,会怎么样…
Spotify 数据:能量与可跳舞性——Aleia Knight
有许多模型和方法可以找出最适合无法直观解释的数据的线。
我们将使用线性回归的两种主要方法来寻找最佳拟合线。
- 均方误差
- 梯度下降
设置
总是从加载必要的导入并创建数据和变量开始
import pandas as pd #For data handling
import matplotlib.pyplot as plt #For data visualization
import numpy as np #For calculationsdata = pd.read_csv('data.csv') #Spotify data
x = data['danceability']
y = data['energy']
展望未来,统计和微积分中有许多复杂的数学公式和技术。访问 偏导数简介 和*线性回归 n 快速复习概念和公式。*
使用渐变下降
这种技术可以被认为是一种优化的方法,通过寻找与理想值的最小误差来找到最佳拟合线。
giphy.com
这个过程的第一步是找到我们的最佳斜率和截距。也就是后来所知的“权重”,只要我们的 x 和 y 数据保持不变,这些值在这些计算中将保持不变。
为了计算斜率和截距,我们需要以下公式来计算权重的均方误差, w1 和 w0。
**
公式— Aleia Knight
左侧图像显示了数学符号到相关变量的转换。右侧是用于计算**【w1(斜率)*** 和 w0(截距) 的公式。这个过程如果手工完成,需要深入的微积分知识,特别是解多变量方程,求偏导数。*
查找资源:
一旦我们找到 w1 和 w0 的值,我们就可以使用这些公式来求解 w1 和 w0 的实际值。
这是一个漫长的过程,但是有了 numpy 库,我们能够简化这个过程。
这将打印出语句(对于特定的示例)。
*Slope is / Weight 1 : 0.05034768176424329
Intercept / Weight 2 is: 0.6504410345649969
(0.05034768176424329, 0.6504410345649969)*
坚持这两个价值观!
理解y_pred=w1*x+w0
的一个更简单的方法是把它想成更熟悉的形式y=mx+b
。y_pred
是基于我们之前找到的 w1 和 w0 的预测线。
绘制这条线,我们看到:
Spotify 数据:能量与可跳舞性——梯度下降——Aleia Knight
直觉上,这似乎有点奇怪,但当我们看聚类(另一种分析风格)时,这确实是有意义的,并且我们的数据似乎确实有较低的正趋势。
我们可以用均方差来分析这条线的有效性。
均方误差
这个概念很简单,因为我们看到的是实际点和预测点之间的差异。
*actual = 1
predicted = 3
error = 2*
这是在所有数据点上完成的(求和),除以点的数量(平均值),并找到平方(平方)以消除任何负数。值 0 是不可能的,但是越接近零越好。这反映在代码中。
MSE via my calculations is: 0.04412711932424555
非常接近零,这太棒了!这告诉我们,误差值非常低,所以我们的线是相当准确的。
giphy.com
这种算法和许多其他算法一样,可以用于计算机科学、数据科学和技术涉及的任何领域。
正是通过使用这些能力,我们能够更快、更有效地处理数据,并利用它来帮助我们走向未来。
下降法——最速下降法和共轭梯度法
数学解释
让我们从这个方程开始,我们想解出 x:
medium.com](https://medium.com/@msdata/descent-method-steepest-descent-and-conjugate-gradient-in-python-85aa4c4aac7b)
让我们从这个方程开始,我们想解出 x:
当 A 是对称正定时,解 x 最小化下面的函数(否则,x 可能是最大值)。这是因为 f(x)的梯度,∇f(x) = Ax- b .而当 Ax=b 时,∇f(x)=0 因而 x 是函数的最小值。
在这篇文章中,我将向你展示两种求 x 的方法——最速下降法和共轭梯度法。
最速下降法
下降法的主要思想是,我们从 x 的起点开始,试图找到下一个更接近解的点,迭代这个过程,直到找到最终解。
例如,在步骤 k,我们在点𝑥(𝑘).我们如何决定下一步去哪里?我们应该往哪个方向走?我们应该去多少?
让我们假设我们决定去的方向是 p(k ),我们沿着这个方向走多远是𝛼.那么下一个数据点可以写成:
对于每一步,最速下降法都希望朝着最陡的斜坡下降,在那里最有效(这里 r 表示残差):
一旦我们决定了前进的方向,我们要确保在这个方向上最小化函数:
现在我们可以计算下一个数据点的 x 和残差:
这基本上是最速下降法背后的数学原理。通常我们会给残差一个停止准则,然后我们迭代这个过程,直到到达停止点。
对于 Python 实现,请查看:
让我们从这个方程开始,我们想解出 x:
medium.com](https://medium.com/@msdata/descent-method-steepest-descent-and-conjugate-gradient-in-python-85aa4c4aac7b)
共轭梯度法
最速下降法是伟大的,我们在每一步的方向上最小化函数。但这并不能保证,我们要最小化的方向,来自于所有之前的方向。这里我们引入一个非常重要的术语共轭方向。方向 p 是共轭方向,如果它们具有下列性质(注 A 是对称正定的):
只有当前方向 p 与所有先前方向共轭时,下一个数据点才会在所有先前方向的跨度内最小化函数。
问题是,当 p 必须是共轭时,我们如何计算搜索方向 p?
记住最陡下降选择了最陡斜率,也就是每步的残差®。我们知道这是一个好的选择。不如我们找一个最接近最陡下降方向的 A 共轭方向,也就是说,我们最小化向量(r-p)的 2 范数。
通过计算,我们知道当前方向是当前残差和上一个方向的组合。
因为α-共轭方向的性质:
然后我们可以计算𝛾_𝑘:
总之,共轭梯度法如下:
同样,对于 Python 实现,请查看:
让我们从这个方程开始,我们想解出 x:
medium.com](https://medium.com/@msdata/descent-method-steepest-descent-and-conjugate-gradient-in-python-85aa4c4aac7b)
现在你知道如何用最速下降法和共轭梯度法解线性方程组了!尽情享受吧!
参考:
这一单元可能是这门课程中技术难度最大的单元。为了完整起见,我们在这里给出了细节,但是…
www.cs.utexas.edu](https://www.cs.utexas.edu/users/flame/laff/alaff/chapter08-important-observations.html)
描述和推断统计
来源: Unsplash
如果你是数据科学领域的新手,你会知道缺乏统计知识有时会非常令人沮丧并阻碍进步。至少了解统计学的基础知识变得非常重要。在这篇文章中,我们将回到基础。我们将探讨两种主要的统计类型——描述性统计和推断性统计。你可能从名字就能看出这两种类型代表什么。但是我们还是会明白他们的意思。我们先从描述性统计开始。
描述统计学
描述性统计用于将大量数据描述为摘要。想象一下,你正在研制一种护发产品,据说可以减少男性秃顶。所以你想知道男性秃顶的首要原因是什么。但是你不能到处去问现在地球上每一个秃顶的人是什么导致了他们的秃顶,这是不切实际的。所以你在你的当地社区,或者扩展社区,选择一群秃顶的男人。为此你总共有 500 个人。你对这些人做了一项调查,大致了解了秃顶的各种原因。然后举一反三,可以说,适用于整个地球的秃子人口。你调查的一小部分人被称为样本。无论您收集了什么数据,都将进行汇总,汇总结果可能如下所示:
如你所见,即使你收集了 500 个答案,你也只总结了四行。这是描述性统计。这个统计数字代表了全部人口。但这并没有停止。你可以得到更多的信息。你可以计算你收集的数据的平均值、中间值、最大值和最小值。或者甚至可以绘制峰度图来更好地理解传播。可以看看偏斜度。你可以利用现有的数据做很多事情,这些数据可以描述你正在处理的问题。
这是描述性统计。我希望这是足够的描述。🙂
推断统计学
顾名思义,推断统计学是用来从我们所拥有的数据中推断或推出信息或结论的。我们将继续前面的男性秃顶的例子。在前面的章节中,我们从我们得出的描述性统计数据中看到,根据这些人的说法,秃顶的主要原因是遗传的。那是没有药可治的。所以我们会看到下一个主要原因,那就是水质。我们可以推断,如果我们改善人们淋浴用水的质量,我们可能会减少男性秃顶。我们可以带一些药物来减少水中的污染物,或者向水中添加矿物质来改善水质,这可能有助于减少脱发。
我希望你明白我的意思。任何时候你使用数据或统计来得出一个关于假设的结论,或者根据描述性统计的早期阶段来做预测,你都是在做推断统计。
原载于 2020 年 1 月 30 日 我的个人博客 。
手工描述性统计
照片由 Pop &斑马拍摄
介绍
T 他的文章解释了如何手工计算主要的描述性统计数据以及如何解释它们。要了解如何在 R 中计算这些度量,请阅读文章“R中的描述性统计”。
描述统计学(广义上)是统计学的一个分支,旨在总结、描述和呈现一系列数值或数据集。由于难以识别数据中的任何模式,没有任何准备或没有任何汇总措施的长系列值通常不能提供信息。下面是一个 100 个成年人的身高(单位为厘米)的例子:
188.7 、 169.4 、 178.6 、 181.3 、 179 、 173.9 、 171.9 、 157.2 、 173.3 、 187.1 、 194 、、170.7 177.1 、 171.4 、 182.6 、 167.7 、 161.3 、 179.3 ,, 171.3 、 176.9 、 180.8 、 189 、 167.7 、188【、 【t160.1】、【t1163.6】、、【165】、、【167】、【t168.1】【t1169.1】、【t170.1】【、【t176.0】**
面对这一系列,任何人都很难(甚至不可能)理解这些数据,并在合理的时间内对这些成年人的规模有一个清晰的看法。描述性统计允许进行总结,从而对数据有更好的了解。当然,通过一项或几项措施汇总数据,难免会丢失一些信息。然而,在许多情况下,丢失一些信息通常更好,但反过来获得一个概述。
描述性统计通常是任何统计分析的第一步,也是重要的一部分。它允许通过检测潜在的异常值(即看起来与其余数据分离的数据点)、收集或编码错误来检查数据质量。它也有助于“理解”数据,如果描述性统计数据表现良好,它已经是进一步分析的良好起点。
位置与离差度量
几种不同的方法(如果我们正在分析一个样本,称为统计)被用来总结数据。其中一些给出了关于数据位置的理解,另一些给出了关于数据分散的理解。在实践中,为了以最简洁但完整的方式总结数据,这两种类型的衡量标准经常一起使用。我们用下面的图来说明这一点,图中代表了分成两组(每组 50 人)的 100 个人的身高(以厘米为单位):
黑线对应的是平均值。两组的平均身高(厘米)相似。然而,很明显,这两个组的高度离散度非常不同。因此,如果单独呈现位置或分散测量,通常是不够的,呈现两种类型测量的多个统计数据是一种好的做法。
在接下来的章节中,我们将详细介绍最常见的位置和离差测量,并举例说明。
位置
位置测量允许查看数据的“位置”和周围的值。换句话说,位置测量给出了对什么是中心趋势,即数据作为一个整体的“位置”的理解。它包括以下统计数据(其他数据也存在,但我们只关注最常见的数据):
- 最低限度
- 最高的
- 意思是
- 中位数
- 第一四分位数
- 第三个四分位数
- 方式
在接下来的章节中,我们将详细介绍并手动计算它们。
最小值和最大值
最小值(min)和最大值(max)分别是最小值和最大值。给定 6 个成人样本的身高(以厘米为单位):
188.7 、 169.4 、 178.6 、 181.3 、 179 和 173.9
最小 169.4 cm,最大 188.7 cm。这两个基本统计数据清楚地显示了这 6 个成年人的最小和最高的尺寸。
平均
均值,也称为平均值,可能是最常见的统计数据。它给出了平均值的概念,即数据的中心值,或者说重心。通过将所有值相加并将该和除以观察次数(表示为 nn)来得到平均值:
下面是平均值的直观表示:
卑鄙。资料来源:加州大学卢万分校 LFSAB1105
给定上述 6 名成人样本,平均值为:
总之,平均尺寸,即 6 名成人样本的平均尺寸为 178.48 厘米(四舍五入到小数点后两位)。
中位数
中位数是位置的另一种度量,因此它也给出了关于数据集中趋势的想法。对中位数的解释是,中位数以下的观察值与中位数以上的一样多。换句话说,50%的观察值低于中位数,50%的观察值高于中位数。中间值的直观表示如下:
中位数。资料来源:加州大学卢万分校 LFSAB1105
计算中值的最简单方法是首先将数据从最低到最高排序(即按升序),然后取中间点作为中值。从排序后的值中,对于奇数个观察值,中间点很容易找到:它是下面的观察值与上面的一样多的值。仍然从排序的值来看,对于偶数个观察值,中点正好在两个中间值之间。形式上,排序后,中位数是:
其中 x 的下标表示排序数据的编号。公式看起来比实际要难,所以让我们看两个具体的例子。
奇数个观察值
给定从引言中呈现的 100 个成人中选取的 7 个成人的样本的身高:
188.9 、 163.9 、 166.4 、 163.7 、 160.4 、 175.8 和 181.5
我们首先从低到高排序:
160.4 , 163.7 , 163.9 , 166.4 , 175.8 , 181.5 和 188.9
假设观察数 nn 为奇数(因为 n=7),中值为
所以我们从排序后的值中取第四个值,对应于 166.4。总之,这 7 个成年人的平均尺寸是 166.4 厘米。可以看到,166.4 以下有 3 个观测值,166.4 cm 以上有 3 个观测值。
偶数个观察值
现在让我们来看看观察次数为偶数时的情况,这比观察次数为奇数时的情况稍微复杂一些。给定 6 个成人样本的身高:
188.7 、 169.4 、 178.6 、 181.3 、 179 和 173.9
我们按升序对值进行排序:
169.4 、 173.9 、 178.6 、 179 、 181.3 和 188.7
假设观察数量 nn 为偶数(因为 n=6),则中位数为
因此,我们将排序后的值中的第三个和第四个值相加,并将这个和除以 2(这相当于取这两个中间值的平均值):
总之,这 6 个成年人的平均尺寸是 178.8 厘米。再次注意,下面的观察值与上面的 178.8 厘米一样多。
平均值与中值
虽然平均值和中间值经常彼此相对接近,但是它们不应该混淆,因为它们在不同的环境中都有优点和缺点。除了几乎每个人都知道(或至少听说过)平均值这一事实之外,它还有一个优点,即它为每个不同的数据系列提供了一个独特的图像。然而,它的缺点是均值对异常值(即极值)敏感。另一方面,中位数的优势在于它可以抵抗异常值,而不方便的是,对于非常不同的数据系列,它可能是完全相同的值(因此对于数据来说不是唯一的)。
为了说明“对局外人来说是明智的”这个论点,考虑一下酒吧里的三个朋友比较他们的工资。他们的工资分别为 1800 、 2000 和 2100 €,平均工资为 1967 €。他们的一个朋友(碰巧也是比尔·盖茨的朋友)加入了他们的酒吧。他们现在的工资分别是 1800 、 2000 、 2100 和 1000000 €。这四个朋友的平均工资现在是 251475€,相比之下,1967 年没有这位富有朋友的€的平均工资是 30。虽然说这四个朋友的平均工资是 251475 €在统计学上是正确的,但是你会承认这个标准并不能代表这四个朋友的真实工资,因为他们中的三个挣得比平均工资少得多。正如我们刚刚看到的,平均值对异常值是敏感的。另一方面,如果我们报告中位数,我们看到 3 个第一朋友的中位数工资是 2000 €,4 个朋友的中位数工资是 2050 €。正如您在本例中看到的,中位数对异常值不敏感,对于具有这种极值的系列,中位数比平均值更合适,因为它通常能更好地表示数据。(*注:*这个例子也说明了绝大多数人的收入是如何低于新闻报道的平均工资的。然而,这超出了本文的范围。)
给定前面的例子,然后可以选择总是使用中间值而不是平均值。然而,中位数有它自己的不便之处,而这是平均数所没有的:与平均数相比,中位数不那么独特,也不那么具体。考虑以下数据,代表参加统计和经济学考试的 5 名学生的成绩:
经济学和统计学的成绩中位数是一样的(中位数= 10 )。因此,如果我们只计算中位数,我们可以得出结论,学生在经济学和统计学方面表现一样好。然而,尽管两个班级的中位数完全相同,但很明显,学生在经济学方面的表现要比统计学好。事实上,经济学的平均成绩是 13.6 ,统计学的平均成绩是 8.6 。我们刚刚在这里展示的是,中位数仅基于一个单一值,即中间值,或者如果有偶数个观察值,则基于两个中间值,而平均值基于所有值(因此包含更多信息)。因此,中值对于异常值是不敏感的,但是对于不同的数据系列,它也不是唯一的(即,不特定的),而对于不同的数据系列,平均值更可能是不同的和唯一的。这两种测量之间在特异性和唯一性方面的差异可能使平均值对没有异常值的数据更有用。
总之,根据上下文和数据,报告平均值或中值,或者两者都报告通常更有意思。关于两个最重要的位置测量值之间的比较,最后一点需要注意的是,当平均值和中值相等时,数据的分布通常可被视为遵循正态分布(也称为高斯分布)。
第一和第三四分位数
第一个和第三个四分位数在某种意义上类似于中位数,它们也将观察值分为两部分,只是这两部分不相等。提醒一下,中位数是把数据分成相等的两部分(50%的观测值在中位数以下,50%在中位数以上)。第一个四分位数对观测值进行分割,使得在第一个四分位数的下方有 25%的观测值**,而在上方有 75%的观测值。第三个四分位数,正如你现在已经猜到的,代表 75%的观察值在它下面,因此 25%的观察值在它上面。有几种方法可以计算第一个和第三个四分位数(有时会有细微的差别,例如 R 使用了不同的方法),但我认为手动计算这些统计数据时最简单的方法是:**
- 按升序对数据进行排序
- 计算 0.25⋅n 和 0.75⋅n(即观察次数的 0.25 和 0.75 倍)
- 将这两个数字四舍五入到下一个整数
- 这两个数字分别代表第一个和第三个四分位数的等级(在已排序的序列中)
奇数和偶数观测值的步骤相同。以下是以下系列的一个示例,代表 9 个成年人的身高(以厘米为单位):
170.2 , 181.5 , 188.9 , 163.9 , 166.4 , 163.7 , 160.4 , 175.8 和 181.5
我们先从最低到最高排序:
160.4 , 163.7 , 163.9 , 166.4 , 170.2 , 175.8 , 181.5 , 181.5 和 188.9
有 9 个观察值,所以
向上取整得到 3 和 7,分别代表第一个和第三个四分位数的等级。因此,第一个四分位数是 163.9 厘米,第三个四分位数是 181.5 厘米。
总之,25%的成年人身高低于 163.9 厘米(因此他们中的 75%高于 163.9 厘米),而 75%的成年人身高低于 181.5 厘米(因此他们中的 25%高于 181.5 厘米)。
q0.25、q0.75 和 q0.5
请注意,第一个四分位数表示为 q0.25,第三个四分位数表示为 q0.75(其中 q 代表四分位数)。如您所见,中位数实际上是第二个四分位数,因此有时也表示为 q0.5。
关于十分位数和百分位数的注记
十分位数和百分位数类似于四分位数,只是它们将数据分成 10 和 100 等份。例如,第四个十分位数(q0.4)是这样一个值,它下面有 40%的观察值,因此上面有 60%的观察值。此外,第 98 百分位(q0.98,有时也表示为 P98)是这样的值,即有 98%的观察值低于它,因此有 2%的观察值高于它。百分位数通常用于婴儿的体重和身高,为父母提供关于他们的孩子与同年龄的其他孩子相比的准确信息。
方式
数列的众数是最常出现的数值。换句话说,它是出现次数最多的值。考虑到 9 个成年人的身高:
170 , 168 , 171 , 170 , 182 , 165 , 170 , 189 和 167
模式为 170,因为它是最常见的值,出现 3 次。所有其他值只出现一次。总之,这个样本的大多数成年人身高都是 170 cm。请注意,一个系列可能没有模式(例如, 4 、 7 、 2 和 10 )或一个以上的模式(例如, 4 、 2 、 2 、 8 、 11 和 11 )。
具有两种模式的数据通常被称为双峰数据,具有两种以上模式的数据通常被称为多峰数据,这与具有一种模式的序列被称为单峰数据相反。
定性变量模式
与一些只能计算定量变量(例如平均值)的描述性统计不同,可以计算定量和定性变量的模式(如果您不记得区别,请参见不同类型变量和的概述)。
给定上述 9 名成人的眼睛颜色:
棕色,棕色,棕色,棕色,蓝色,蓝色,蓝色,棕色和绿色
众数是棕色的,所以这个样本的大部分成年人眼睛都是棕色的。
散布
所有以前的描述性统计有助于了解数据的位置和方位。我们现在介绍最常见的离差度量,这有助于了解数据的离差和可变性(分布被压缩或拉伸的程度):
- 范围
- 标准偏差
- 差异
- 四分位差
- 变异系数
至于位置测量,我们逐个手工详细计算这些统计数据。
范围
范围是最大值和最小值之间的差值:
范围=最大最小
给定我们的 6 名成人样本的身高(厘米):
188.7 、 169.4 、 178.6 、 181.3 、 179 和 173.9
范围是 188.7-169.4 = 19.3 厘米。该范围的优点是计算起来非常容易,并且它给出了数据中可能值的精确概念。缺点是它只依赖于两个最极端的值。
标准偏差
标准差是统计学中最常见的离差度量。像位置测量的平均值一样,如果我们必须给出一个统计量来概括数据的分布,它通常是标准差。顾名思义,标准差告诉我们什么是数据的“正常”偏差。它实际上是计算与平均值的平均偏差。标准差越大,数据越分散。相反,标准差越小,数据越集中在平均值附近。下面是标准差的直观表示:
标准差。资料来源:加州大学卢万分校 LFSAB1105
标准差比之前的统计数据要复杂一些,因为有两个公式取决于我们面对的是样本还是总体。总体包括来自特定组的所有成员、所有可能的结果或感兴趣的度量。样本由从总体中抽取的一些观察值组成,即总体的一部分或子集。例如,人口可能是“所有居住在比利时的人”,样本可能是“一些居住在比利时的人”。如果你想了解更多,请阅读这篇关于总体和样本的差异的文章。
总体的标准偏差
总体的标准差,用σ表示,为:
从公式中可以看出,标准差实际上是数据与其均值μμ的平均偏差。注意观察值和平均值之间的差的平方,以避免负差被正差补偿。
为了简单起见,假设一个只有 3 个成年人的群体(步骤与大群体相同,只是计算时间更长)。低于他们的身高(厘米):
160.4 、 175.8 和 181.5
平均值为 172.6(四舍五入到 1 位小数)。因此,标准偏差为:
总之,这三个成年人身高的标准偏差是 8.91 厘米。这意味着,平均而言,该人群中成年人的身高偏离平均值 8.91 厘米。
样本的标准偏差
样本的标准差类似于总体的标准差,只是我们用 n1n 1 而不是 nn 来除,用 s 表示:
现在想象一下,上一节中出现的 3 个成年人是一个样本,而不是一个群体:
160.4 、 175.8 和 181.5
平均值仍然是 172.6(四舍五入到 1 位小数),因为无论是总体还是样本,平均值都是一样的。标准偏差现在是:
总之,这三个成年人身高的标准偏差是 10.92 厘米。对人口的解释是一样的。
差异
方差就是标准差的平方。换句话说,标准差是方差的平方根。我们也区分总体和样本的方差。
总体方差
用σ^2 表示的总体方差为:
如您所见,方差公式与标准差公式相同,只是方差的平方根被去掉了。记住我们三个成年人的身高:
160.4 、 175.8 和 181.5
标准偏差是 8.91 厘米,所以这些成年人身高的方差是 8.91^2 = 79.39 cm^2(见下文为什么方差的单位是 unit^2).
样本的方差
同样,样本的方差类似于总体的方差,只是我们除以 n1 而不是 n,它表示为 s^2:
再次假设上一节中的 3 个成年人是一个样本,而不是一个群体:
160.4 、 175.8 和 181.5
该样本的标准偏差为 10.92 厘米,因此这些成人身高的方差为 119.14 cm^2.
标准差与方差
标准差和方差通常可以互换使用,它们都通过测量观察值与平均值的距离来量化给定数据集的分布。但是,标准差可以更容易地解释,因为标准差的单位与数据的测量单位相同(虽然它是方差的 unit^2)。按照我们以厘米为单位的成人身高示例,标准偏差以厘米为单位测量,而方差以 cm^2.为单位测量事实上,标准偏差与初始测量单位保持相同的单位,这使得它更容易解释,因此在实践中更常用。
记号
为完整起见,在下表中显示了总体和样本的方差和标准差的不同符号:
四分位间距
还记得前面提出的第一个 q0.25 和第三个四分位数 q0.75 吗?四分位距是使用四分位来衡量数据离差的另一种方法。这是第三个四分位数和第一个四分位数之间的差异:
IQR = q 0.75 q 0.25
考虑到第一个和第三个四分位数部分给出的 9 名成人的身高:
170.2 , 181.5 , 188.9 , 163.9 , 166.4 , 163.7 , 160.4 , 175.8 和 181.5
第一个四分位数为 163.9 厘米,第三个四分位数为 181.5 厘米。IQR 是这样的:
IQR = 181.5-163.9 = 17.6
总之,四分位距是 17.6 厘米。四分位范围实际上是中间数据的范围(因为它是较高值和较低值之间的差值)。下图可能有助于更好地理解 IQR 和四分位数:
IQR,第一和第三季度。资料来源:加州大学卢万分校 LFSAB1105
变异系数
最后一个离差度量是变异系数。变异系数表示为 CV,是标准偏差除以平均值。形式上:
考虑 4 个成人样本的身高:
163.7 、 160.4 、 175.8 和 181.5
平均值为 170.35 厘米,标准偏差为 9.95 厘米。(找到相同的价值观作为练习!)变异系数为
综上,变异系数为 5.8%。注意,根据经验,变异系数大于 15%通常意味着数据是异质的,而变异系数等于或小于 15%意味着数据是同质的。假设在这种情况下变异系数等于 5.8%,我们可以得出结论,这 4 个成年人在身高方面是同质的。
变异系数与标准偏差
虽然变异系数不为公众所知,但事实上,在进行描述性统计时,它是值得提出的。
标准偏差应始终在数据平均值的背景下理解,并取决于其单位。标准偏差的优点是,它可以告诉我们平均数据与测量数据的单位平均值相差多远。当考虑具有相同单位和近似相同平均值的变量时,标准差是有用的。然而,当用不同的单位或相差很大的平均值来比较变量时,标准差就没那么有用了。例如,一个标准偏差为 10 厘米的变量不能与一个标准偏差为 10€的变量进行比较,从而得出哪一个变量最分散的结论。
变异系数是具有相同单位的两个统计值的比率。因此,它没有单位,与测量数据的单位无关。由于是无单位的,所以可以比较对具有不同单位或差异很大的平均值的数据集或变量计算的变异系数,以最终得出哪个数据或变量更分散(或更不分散)的结论。例如,考虑 10 名女性的样本,她们的身高为厘米,工资在€。身高和工资的变异系数分别为 0.032 和 0.061。我们可以得出结论,相对于她们各自的平均值,这些女性的工资变化大于身高变化(因为工资的变异系数大于身高的变异系数)。
这就结束了一篇比较长的文章,感谢阅读!我希望这篇文章能帮助你理解和手工计算不同的描述性统计。如果您想了解如何在 R 中计算这些度量,请阅读文章“R中的描述性统计”。
和往常一样,如果您有与本文主题相关的问题或建议,请将其添加为评论,以便其他读者可以从讨论中受益。
相关文章:
原载于 2020 年 1 月 18 日 https://statsandr.com。
R 中的描述统计
本文解释了如何计算 R 中的主要描述性统计数据,以及如何以图形方式呈现它们。
照片由鲁特森·齐默曼拍摄
介绍
这篇文章解释了如何计算 R 中的主要描述性统计数据,以及如何用图表的方式展示它们。要了解每个描述性统计背后的推理,如何手工计算它们以及如何解释它们,请阅读文章“手工描述性统计”。
简要回顾一下那篇文章中所说的内容,描述统计学(广义上的)是统计学的一个分支,旨在总结、描述和呈现一系列值或数据集。描述性统计通常是任何统计分析的第一步和重要部分。它允许检查数据的质量,并有助于通过对数据有一个清晰的概述来“理解”数据。如果表述得好,描述性统计已经是进一步分析的良好起点。有许多方法可以总结一个数据集。它们分为两种类型:
- 位置测量和
- 分散测量
位置测量提供了对数据集中趋势的理解,而分散测量提供了对数据扩散的理解。在本文中,我们只关注 R 中最常见的描述性统计及其可视化的实现(如果认为合适的话)。有关每项措施的目的和用法的更多信息,请参见在线或上述文章中的。
数据
我们在整篇文章中使用数据集iris
。这个数据集在 R 中是默认导入的,你只需要通过运行iris
来加载它:
dat <- iris # load the iris dataset and renamed it dat
下面是该数据集及其结构的预览:
head(dat) # first 6 observations## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1 5.1 3.5 1.4 0.2 setosa
## 2 4.9 3.0 1.4 0.2 setosa
## 3 4.7 3.2 1.3 0.2 setosa
## 4 4.6 3.1 1.5 0.2 setosa
## 5 5.0 3.6 1.4 0.2 setosa
## 6 5.4 3.9 1.7 0.4 setosastr(dat) # structure of dataset## 'data.frame': 150 obs. of 5 variables:
## $ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
## $ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
## $ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
## $ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
## $ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
该数据集包含 150 个观察值和 5 个变量,代表萼片和花瓣的长度和宽度以及 150 种花的种类。萼片和花瓣的长度和宽度是数值变量,物种是一个有 3 个水平的因子(在变量名称后用num
和Factor w/ 3 levels
表示)。如果需要刷新,请参见 R 中的不同变量类型。
关于图,我们给出了默认的图和著名的{ggplot2}
包中的图。来自{ggplot2}
包的图形通常具有更好的外观,但是它需要更高级的编码技能(参见文章“R 中的图形与 ggplot2 ”以了解更多信息)。如果你需要发布或分享你的图表,我建议如果可以的话使用{ggplot2}
,否则默认的图表就可以了。
提示:我最近从 **{esquisse}**
插件中发现了 ggplot2 构建器。看看你如何从 [**{ggplot2}**](https://www.statsandr.com/blog/rstudio-addins-or-how-to-make-your-coding-life-easier/)
包 中轻松地 画出图形,而不必自己编码。
本文中显示的所有图都可以定制。例如,可以编辑标题、x 和 y 轴标签、颜色等。然而,定制图超出了本文的范围,所以所有的图都没有任何定制。感兴趣的读者可以在网上找到大量的资源。
最小值和最大值
通过min()
和max()
功能可以找到最小值和最大值:
min(dat$Sepal.Length)## [1] 4.3max(dat$Sepal.Length)## [1] 7.9
或者是range()
功能:
rng <- range(dat$Sepal.Length)
rng## [1] 4.3 7.9
直接给出最小值和最大值。注意,range()
函数的输出实际上是一个包含最小值和最大值的对象(按照这个顺序)。这意味着您实际上可以通过以下方式获得最小值:
rng[1] # rng = name of the object specified above## [1] 4.3
最大值为:
rng[2]## [1] 7.9
这提醒我们,在 R 中,通常有几种方法可以达到相同的结果。使用最短代码段的方法通常是首选,因为较短的代码段不容易出现编码错误,并且可读性更好。
范围
正如您所猜测的,通过从最大值中减去最小值,可以很容易地计算出范围:
max(dat$Sepal.Length) - min(dat$Sepal.Length)## [1] 3.6
据我所知,没有默认的函数来计算范围。但是,如果您熟悉用 R 编写函数,您可以创建自己的函数来计算范围:
range2 <- function(x) {
range <- max(x) - min(x)
return(range)
}range2(dat$Sepal.Length)## [1] 3.6
这相当于上面给出的最大最小值。
平均
平均值可以用mean()
函数计算:
mean(dat$Sepal.Length)## [1] 5.843333
温馨提示:
- 如果数据集中至少有一个缺失值,使用
mean(dat$Sepal.Length, na.rm = TRUE)
计算排除 NA 后的平均值。该参数可用于本文中介绍的大多数函数,而不仅仅是平均值 - 对于截断的平均值,使用
mean(dat$Sepal.Length, trim = 0.10)
并根据您的需要更改trim
参数
中位数
通过median()
函数可以计算出中值:
median(dat$Sepal.Length)## [1] 5.8
或者使用quantile()
功能:
quantile(dat$Sepal.Length, 0.5)## 50%
## 5.8
因为 0.5 阶的分位数(q0.5)对应于中值。
第一和第三四分位数
由于使用了quantile()
函数,并通过将第二个参数设置为 0.25 或 0.75,可以计算出第一个和第三个四分位数的中值:
quantile(dat$Sepal.Length, 0.25) # first quartile## 25%
## 5.1quantile(dat$Sepal.Length, 0.75) # third quartile## 75%
## 6.4
您可能已经看到,上面的结果与您手工计算第一个和第三个四分位数的结果略有不同。这是正常的,有许多方法来计算它们(R 实际上有 7 种方法来计算分位数!).然而,这里和文章“手工描述性统计”中介绍的方法是最简单和最“标准”的方法。此外,两种方法之间的结果没有显著变化。
其他分位数
正如您已经猜到的,任何分位数也可以用quantile()
函数来计算。例如,第四个十分位数或第 98 个百分位数:
quantile(dat$Sepal.Length, 0.4) # 4th decile## 40%
## 5.6quantile(dat$Sepal.Length, 0.98) # 98th percentile## 98%
## 7.7
四分位间距
四分位数间距(即第一个四分位数和第三个四分位数之间的差值)可以用IQR()
函数计算:
IQR(dat$Sepal.Length)## [1] 1.3
或者再次使用quantile()
功能:
quantile(dat$Sepal.Length, 0.75) - quantile(dat$Sepal.Length, 0.25)## 75%
## 1.3
如前所述,如果可能的话,通常建议使用最短的代码来获得结果。因此,最好使用IQR()
函数来计算四分位数范围。
标准偏差和方差
使用sd()
和var()
函数计算标准偏差和方差:
sd(dat$Sepal.Length) # standard deviation## [1] 0.8280661var(dat$Sepal.Length) # variance## [1] 0.6856935
请记住文章手动描述性统计中的内容,无论是计算样本还是总体,标准差和方差都是不同的(参见样本和总体之间的差异)。在 R 中,计算标准差和方差时,假设数据代表一个样本(因此分母为 n-1,其中 n 为观察次数)。据我所知,R 中默认没有计算总体的标准差或方差的函数。
*提示:*要同时计算多个变量的标准差(或方差),使用lapply()
和适当的统计数据作为第二个参数:
lapply(dat[, 1:4], sd)## $Sepal.Length
## [1] 0.8280661
##
## $Sepal.Width
## [1] 0.4358663
##
## $Petal.Length
## [1] 1.765298
##
## $Petal.Width
## [1] 0.7622377
命令dat[, 1:4]
选择变量 1 至 4,因为第五个变量是一个定性变量,不能对这类变量计算标准偏差。如有必要,参见 R 中不同数据类型的概述。
摘要
您可以使用summary()
一次计算数据集所有数值变量的最小值、第一四分位数、中值、平均值、第三四分位数和最大值:
summary(dat)## Sepal.Length Sepal.Width Petal.Length Petal.Width
## Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100
## 1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300
## Median :5.800 Median :3.000 Median :4.350 Median :1.300
## Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199
## 3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800
## Max. :7.900 Max. :4.400 Max. :6.900 Max. :2.500
## Species
## setosa :50
## versicolor:50
## virginica :50
##
##
##
*提示:*如果您需要这些分组描述性统计数据,请使用by()
功能:
by(dat, dat$Species, summary)## dat$Species: setosa
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## Min. :4.300 Min. :2.300 Min. :1.000 Min. :0.100
## 1st Qu.:4.800 1st Qu.:3.200 1st Qu.:1.400 1st Qu.:0.200
## Median :5.000 Median :3.400 Median :1.500 Median :0.200
## Mean :5.006 Mean :3.428 Mean :1.462 Mean :0.246
## 3rd Qu.:5.200 3rd Qu.:3.675 3rd Qu.:1.575 3rd Qu.:0.300
## Max. :5.800 Max. :4.400 Max. :1.900 Max. :0.600
## Species
## setosa :50
## versicolor: 0
## virginica : 0
##
##
##
## ------------------------------------------------------------
## dat$Species: versicolor
## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## Min. :4.900 Min. :2.000 Min. :3.00 Min. :1.000 setosa : 0
## 1st Qu.:5.600 1st Qu.:2.525 1st Qu.:4.00 1st Qu.:1.200 versicolor:50
## Median :5.900 Median :2.800 Median :4.35 Median :1.300 virginica : 0
## Mean :5.936 Mean :2.770 Mean :4.26 Mean :1.326
## 3rd Qu.:6.300 3rd Qu.:3.000 3rd Qu.:4.60 3rd Qu.:1.500
## Max. :7.000 Max. :3.400 Max. :5.10 Max. :1.800
## ------------------------------------------------------------
## dat$Species: virginica
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## Min. :4.900 Min. :2.200 Min. :4.500 Min. :1.400
## 1st Qu.:6.225 1st Qu.:2.800 1st Qu.:5.100 1st Qu.:1.800
## Median :6.500 Median :3.000 Median :5.550 Median :2.000
## Mean :6.588 Mean :2.974 Mean :5.552 Mean :2.026
## 3rd Qu.:6.900 3rd Qu.:3.175 3rd Qu.:5.875 3rd Qu.:2.300
## Max. :7.900 Max. :3.800 Max. :6.900 Max. :2.500
## Species
## setosa : 0
## versicolor: 0
## virginica :50
##
##
##
其中参数是数据集、分组变量和汇总函数的名称。请遵循此顺序,如果不遵循此顺序,请指定参数的名称。
如果您需要更多的描述性统计数据,请使用{pastecs}
包中的stat.desc()
:
library(pastecs)
stat.desc(dat)## Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## nbr.val 150.00000000 150.00000000 150.0000000 150.00000000 NA
## nbr.null 0.00000000 0.00000000 0.0000000 0.00000000 NA
## nbr.na 0.00000000 0.00000000 0.0000000 0.00000000 NA
## min 4.30000000 2.00000000 1.0000000 0.10000000 NA
## max 7.90000000 4.40000000 6.9000000 2.50000000 NA
## range 3.60000000 2.40000000 5.9000000 2.40000000 NA
## sum 876.50000000 458.60000000 563.7000000 179.90000000 NA
## median 5.80000000 3.00000000 4.3500000 1.30000000 NA
## mean 5.84333333 3.05733333 3.7580000 1.19933333 NA
## SE.mean 0.06761132 0.03558833 0.1441360 0.06223645 NA
## CI.mean.0.95 0.13360085 0.07032302 0.2848146 0.12298004 NA
## var 0.68569351 0.18997942 3.1162779 0.58100626 NA
## std.dev 0.82806613 0.43586628 1.7652982 0.76223767 NA
## coef.var 0.14171126 0.14256420 0.4697441 0.63555114 NA
通过在前面的函数中添加参数norm = TRUE
,您可以获得更多的统计数据(即偏度、峰度和正态性检验)。请注意,变量Species
不是数字,因此无法计算该变量的描述性统计数据,并显示 NA。
变异系数
变异系数可以通过stat.desc()
(见上表中的coef.var
线)或手动计算得到(记住变异系数是标准偏差除以平均值):
sd(dat$Sepal.Length) / mean(dat$Sepal.Length)## [1] 0.1417113
方式
据我所知,没有找到变量模式的函数。然而,由于函数table()
和sort()
,我们可以很容易地找到它:
tab <- table(dat$Sepal.Length) # number of occurrences for each unique value
sort(tab, decreasing = TRUE) # sort highest to lowest##
## 5 5.1 6.3 5.7 6.7 5.5 5.8 6.4 4.9 5.4 5.6 6 6.1 4.8 6.5 4.6 5.2 6.2 6.9 7.7
## 10 9 9 8 8 7 7 7 6 6 6 6 6 5 5 4 4 4 4 4
## 4.4 5.9 6.8 7.2 4.7 6.6 4.3 4.5 5.3 7 7.1 7.3 7.4 7.6 7.9
## 3 3 3 3 2 2 1 1 1 1 1 1 1 1 1
table()
给出每个唯一值出现的次数,然后sort()
和参数decreasing = TRUE
从最高到最低显示出现的次数。因此变量Sepal.Length
的模式是 5。此代码查找模式也可应用于定性变量,如Species
:
sort(table(dat$Species), decreasing = TRUE)##
## setosa versicolor virginica
## 50 50 50
或者:
summary(dat$Species)## setosa versicolor virginica
## 50 50 50
相互关系
另一个描述性统计是相关系数。相关性衡量两个变量之间的线性关系。
计算 R 中的相关性需要一个详细的解释,所以我写了一篇文章,涵盖了相关性和相关性测试。
相依表
table()
上面介绍的也可以用在两个定性变量上来创建一个列联表。数据集iris
只有一个定性变量,因此我们为这个例子创建一个新的定性变量。我们创建变量size
,如果花瓣的长度小于所有花的中值,则对应于small
,否则对应于big
:
dat$size <- ifelse(dat$Sepal.Length < median(dat$Sepal.Length),
"small", "big"
)
以下是按大小排列的事件摘要:
table(dat$size)##
## big small
## 77 73
我们现在用table()
函数创建两个变量Species
和size
的列联表:
table(dat$Species, dat$size)##
## big small
## setosa 1 49
## versicolor 29 21
## virginica 47 3
或者用xtabs()
功能:
xtabs(~ dat$Species + dat$size)## dat$size
## dat$Species big small
## setosa 1 49
## versicolor 29 21
## virginica 47 3
列联表给出了每个分组的病例数。例如,只有一朵大刚毛藻花,而数据集中有 49 朵小刚毛藻花。
更进一步,我们可以从表中看出,setosa 花的尺寸似乎比 virginica 花大。为了检查大小是否与物种显著相关,我们可以进行独立性的卡方检验,因为两个变量都是分类变量。参见如何用手和 R 中的进行该测试。
请注意,Species
在行中,size
在列中,因为我们在table()
中指定了Species
,然后又指定了size
。如果要切换两个变量,请更改顺序。
代替具有频率(即…病例数)您还可以通过在prop.table()
函数中添加table()
函数来获得每个子组中的相对频率(即比例):
prop.table(table(dat$Species, dat$size))##
## big small
## setosa 0.006666667 0.326666667
## versicolor 0.193333333 0.140000000
## virginica 0.313333333 0.020000000
注意,您也可以通过向prop.table()
函数添加第二个参数来按行或按列计算百分比:1
表示行,或者2
表示列:
# percentages by row:
round(prop.table(table(dat$Species, dat$size), 1), 2) # round to 2 digits with round()##
## big small
## setosa 0.02 0.98
## versicolor 0.58 0.42
## virginica 0.94 0.06# percentages by column:
round(prop.table(table(dat$Species, dat$size), 2), 2) # round to 2 digits with round()##
## big small
## setosa 0.01 0.67
## versicolor 0.38 0.29
## virginica 0.61 0.04
更多高级列联表见高级描述统计部分。
马赛克图
镶嵌图允许可视化两个定性变量的列联表:
mosaicplot(table(dat$Species, dat$size),
color = TRUE,
xlab = "Species", # label for x-axis
ylab = "Size" # label for y-axis
)
镶嵌图显示,对于我们的样本,大小花的比例在三个物种之间明显不同。特别是,海滨锦鸡儿属物种是最大的,而 setosa 属物种是三个物种中最小的(就萼片长度而言,因为变量size
是基于变量Sepal.Length
)。
供您参考,镶嵌图也可以通过{vcd}
包中的mosaic()
功能完成:
library(vcd)mosaic(~ Species + size,
data = dat,
direction = c("v", "h"))
条形图
柱状图只能在定性变量上完成(参见定量变量的差异这里)。柱状图是一种可视化定性变量分布的工具。我们绘制了定性变量size
的柱状图:
barplot(table(dat$size)) # table() is mandatory
您也可以像我们之前做的那样,通过添加prop.table()
来绘制相对频率而不是频率的柱状图:
barplot(prop.table(table(dat$size)))
在{ggplot2}
中:
library(ggplot2) # needed each time you open RStudio
# The package ggplot2 must be installed firstggplot(dat) +
aes(x = size) +
geom_bar()
柱状图
直方图给出了定量变量分布的概念。这个想法是将值的范围分成区间,并计算每个区间内有多少个观察值。直方图有点类似于柱状图,但是直方图用于定量变量,而柱状图用于定性变量。要在 R 中绘制直方图,使用hist()
:
hist(dat$Sepal.Length)
如果您想要更改容器的数量,请在hist()
函数中添加参数breaks =
。一个经验法则(称为斯特奇斯定律)是,仓的数量应该是观察数量的平方根的舍入值。数据集包括 150 个观察值,因此在这种情况下,箱的数量可以设置为 12。
在{ggplot2}
:
ggplot(dat) +
aes(x = Sepal.Length) +
geom_histogram()
默认情况下,箱子的数量为 30。例如,您可以使用geom_histogram(bins = 12)
更改该值。
箱线图
箱线图在描述性统计中非常有用,但往往没有得到充分利用(主要是因为公众没有很好地理解它)。箱线图通过直观显示五个常见位置汇总(最小值、中值、第一/第三四分位数和最大值)以及使用四分位数间距(IQR)标准分类为可疑异常值的任何观察值,以图形方式表示定量变量的分布。IQR 标准意味着 q0.75+1.5⋅IQR 以上和 q0.25−1.5⋅IQR 以下的所有观测值(其中 q0.25 和 q0.75 分别对应于第一和第三四分位数)都被 r 视为潜在异常值。箱线图中的最小值和最大值表示为没有这些可疑异常值。在同一个图上看到所有这些信息有助于对数据的分散和位置有一个良好的初步了解。在绘制数据的箱线图之前,请参见下图,该图解释了箱线图上的信息:
详细箱线图。资料来源:加州大学卢万分校 LFSAB1105
现在我们的数据集有一个例子:
boxplot(dat$Sepal.Length)
为了比较和对比两个或更多组的分布情况,并排显示的箱线图提供的信息甚至更多。例如,我们比较不同物种的萼片长度:
boxplot(dat$Sepal.Length ~ dat$Species)
在{ggplot2}
:
ggplot(dat) +
aes(x = Species, y = Sepal.Length) +
geom_boxplot()
点图
点状图或多或少类似于箱线图,只是观察值以点表示,并且图上没有汇总统计数据:
library(lattice)dotplot(dat$Sepal.Length ~ dat$Species)
散点图
散点图允许检查两个定量变量之间是否有潜在的联系。出于这个原因,散点图经常被用来可视化两个变量之间的潜在关联。例如,当绘制萼片长度和花瓣长度的散点图时:
plot(dat$Sepal.Length, dat$Petal.Length)
这两个变量之间似乎有正相关。
在{ggplot2}
中:
ggplot(dat) +
aes(x = Sepal.Length, y = Petal.Length) +
geom_point()
作为箱线图,散点图在根据因子区分点时提供的信息甚至更多,在这种情况下,物种:
ggplot(dat) +
aes(x = Sepal.Length, y = Petal.Length, colour = Species) +
geom_point() +
scale_color_hue()
QQ 图
对于单个变量
为了检查变量的正态性假设(正态性是指数据遵循正态分布,也称为高斯分布),我们通常使用直方图和/或 QQ 图。 1 如果您需要更新相关内容,请参阅讨论正态分布以及如何评估 R 中的正态假设的文章。直方图前面已经介绍过了,下面是如何绘制 QQ 图:
# Draw points on the qq-plot:
qqnorm(dat$Sepal.Length)
# Draw the reference line:
qqline(dat$Sepal.Length)
或者使用{car}
包中的qqPlot()
函数绘制带置信带的 QQ 图:
library(car) # package must be installed first
qqPlot(dat$Sepal.Length)
## [1] 132 118
如果点靠近参考线(有时称为亨利线)并在置信带内,则可以认为满足正态性假设。点和参考线之间的偏差越大,并且它们越位于置信带之外,满足正态条件的可能性就越小。变量Sepal.Length
似乎不遵循正态分布,因为有几个点位于置信带之外。当面对非正态分布时,第一步通常是对数据应用对数变换,并重新检查经过对数变换的数据是否正态分布。应用对数变换可以通过log()
功能完成。
在{ggpubr}
:
library(ggpubr)
ggqqplot(dat$Sepal.Length)
按组
对于一些统计检验,所有组都需要正态假设。一种解决方案是通过手动将数据集分成不同的组来为每个组绘制 QQ 图,然后为每个数据子集绘制 QQ 图(使用上面所示的方法)。另一个(更简单的)解决方案是用{car}
包中函数qqPlot()
中的参数groups =
为每个组自动绘制一个 QQ 图:
qqPlot(dat$Sepal.Length, groups = dat$size)
在{ggplot2}
中:
qplot(
sample = Sepal.Length, data = dat,
col = size, shape = size
)
也可以仅通过形状或颜色来区分组。为此,删除上述qplot()
函数中的参数col
或shape
之一。
密度图
密度图是直方图的平滑版本,用于相同的概念,即表示数值变量的分布。函数plot()
和density()
一起用于绘制密度图:
plot(density(dat$Sepal.Length))
在{ggplot2}
中:
ggplot(dat) +
aes(x = Sepal.Length) +
geom_density()
相关图
最后一种描述图是相关图,也称为相关图。这种类型的图表比上面给出的图表更复杂,因此将在另一篇文章中详细介绍。参见如何绘制相关图以突出显示数据集中最相关的变量。
高级描述统计学
我们讨论了计算最常见和最基本的描述性统计数据的主要函数。然而,r 中有更多的函数和包来执行更高级的描述性统计。在本节中,我将介绍其中一些函数和包在我们的数据集上的应用。
{summarytools}
套餐
我在 R 语言项目中经常使用的一个描述性统计软件包是[{summarytools}](https://cran.r-project.org/web/packages/summarytools/index.html)
软件包。该包围绕 4 个功能展开:
freq()
对于频率表ctable()
用于交叉制表descr()
用于描述性统计dfSummary()
对于数据帧摘要
对于大多数描述性分析来说,这 4 个功能的组合通常已经足够了。此外,这个包在构建时考虑到了 R Markdown ,这意味着输出在 HTML 报告中表现良好。对于非英语使用者,内置了法语、葡萄牙语、西班牙语、俄语和土耳其语翻译。
在接下来的几节中,我将分别阐述这四个函数。随后的输出在 R Markdown 报告中显示得更好,但在本文中,我将自己限制在原始输出上,因为目标是展示函数如何工作,而不是如何使它们呈现得更好。如果您想在 R Markdown 中以漂亮的方式打印输出,请参见包装的插图中的设置设置。 2
带freq()
的频率表
freq()
功能生成频率表,其中包含频率、比例以及缺失数据信息。
library(summarytools)
freq(dat$Species)## Frequencies
## dat$Species
## Type: Factor
##
## Freq % Valid % Valid Cum. % Total % Total Cum.
## ---------------- ------ --------- -------------- --------- --------------
## setosa 50 33.33 33.33 33.33 33.33
## versicolor 50 33.33 66.67 33.33 66.67
## virginica 50 33.33 100.00 33.33 100.00
## <NA> 0 0.00 100.00
## Total 150 100.00 100.00 100.00 100.00
如果不需要关于缺失值的信息,添加report.nas = FALSE
参数:
freq(dat$Species,
report.nas = FALSE) # remove NA information## Frequencies
## dat$Species
## Type: Factor
##
## Freq % % Cum.
## ---------------- ------ -------- --------
## setosa 50 33.33 33.33
## versicolor 50 33.33 66.67
## virginica 50 33.33 100.00
## Total 150 100.00 100.00
对于只有计数和比例的极简输出:
freq(dat$Species,
report.nas = FALSE, # remove NA information
totals = FALSE, # remove totals
cumul = FALSE, # remove cumuls
headings = FALSE) # remove headings##
## Freq %
## ---------------- ------ -------
## setosa 50 33.33
## versicolor 50 33.33
## virginica 50 33.33
与ctable()
交叉列表
ctable()
函数为分类变量对生成交叉表(也称为列联表)。使用数据集中的两个分类变量:
ctable(x = dat$Species,
y = dat$size)## Cross-Tabulation, Row Proportions
## Species * size
## Data Frame: dat
##
## ------------ ------ ------------ ------------ --------------
## size big small Total
## Species
## setosa 1 ( 2.0%) 49 (98.0%) 50 (100.0%)
## versicolor 29 (58.0%) 21 (42.0%) 50 (100.0%)
## virginica 47 (94.0%) 3 ( 6.0%) 50 (100.0%)
## Total 77 (51.3%) 73 (48.7%) 150 (100.0%)
## ------------ ------ ------------ ------------ --------------
默认情况下显示行比例。要显示列或总比例,分别添加prop = "c"
或prop = "t"
参数:
ctable(x = dat$Species,
y = dat$size,
prop = "t") # total proportions## Cross-Tabulation, Total Proportions
## Species * size
## Data Frame: dat
##
## ------------ ------ ------------ ------------ --------------
## size big small Total
## Species
## setosa 1 ( 0.7%) 49 (32.7%) 50 ( 33.3%)
## versicolor 29 (19.3%) 21 (14.0%) 50 ( 33.3%)
## virginica 47 (31.3%) 3 ( 2.0%) 50 ( 33.3%)
## Total 77 (51.3%) 73 (48.7%) 150 (100.0%)
## ------------ ------ ------------ ------------ --------------
要完全删除比例,请添加参数prop = "n"
。此外,为了只显示最低限度,添加totals = FALSE
和headings = FALSE
参数:
ctable(x = dat$Species,
y = dat$size,
prop = "n", # remove proportions
totals = FALSE, # remove totals
headings = FALSE) # remove headings##
## ------------ ------ ----- -------
## size big small
## Species
## setosa 1 49
## versicolor 29 21
## virginica 47 3
## ------------ ------ ----- -------
这相当于在列联表中执行的table(dat$Species, dat$size)
和xtabs(~ dat$Species + dat$size)
。
为了显示卡方独立性检验的结果,添加chisq = TRUE
参数: 3
ctable(x = dat$Species,
y = dat$size,
chisq = TRUE, # display results of Chi-square test of independence
headings = FALSE) # remove headings##
## ------------ ------ ------------ ------------ --------------
## size big small Total
## Species
## setosa 1 ( 2.0%) 49 (98.0%) 50 (100.0%)
## versicolor 29 (58.0%) 21 (42.0%) 50 (100.0%)
## virginica 47 (94.0%) 3 ( 6.0%) 50 (100.0%)
## Total 77 (51.3%) 73 (48.7%) 150 (100.0%)
## ------------ ------ ------------ ------------ --------------
##
## ----------------------------
## Chi.squared df p.value
## ------------- ---- ---------
## 86.03 2 0
## ----------------------------
p 值接近于 0,所以我们拒绝两个变量之间独立性的零假设。在我们的上下文中,这表明物种和大小是相互依赖的,并且这两个变量之间存在显著的关系。
由于结合了stby()
和ctable()
函数,还可以为第三分类变量的每个级别创建一个列联表。我们的数据集中只有 2 个分类变量,所以让我们使用有 4 个分类变量(即性别、年龄组、吸烟者、患病者)的tabacco
数据集。对于这个例子,我们想要创建一个变量smoker
和diseased
的列联表,并且对于每个gender
:
stby(list(x = tobacco$smoker, # smoker and diseased
y = tobacco$diseased),
INDICES = tobacco$gender, # for each gender
FUN = ctable) # ctable for cross-tabulation## Cross-Tabulation, Row Proportions
## smoker * diseased
## Data Frame: tobacco
## Group: gender = F
##
## -------- ---------- ------------- ------------- --------------
## diseased Yes No Total
## smoker
## Yes 62 (42.2%) 85 (57.8%) 147 (100.0%)
## No 49 (14.3%) 293 (85.7%) 342 (100.0%)
## Total 111 (22.7%) 378 (77.3%) 489 (100.0%)
## -------- ---------- ------------- ------------- --------------
##
## Group: gender = M
##
## -------- ---------- ------------- ------------- --------------
## diseased Yes No Total
## smoker
## Yes 63 (44.1%) 80 (55.9%) 143 (100.0%)
## No 47 (13.6%) 299 (86.4%) 346 (100.0%)
## Total 110 (22.5%) 379 (77.5%) 489 (100.0%)
## -------- ---------- ------------- ------------- --------------
用descr()
描述性统计
descr()
函数生成描述性(单变量)统计数据,其中包含常见的集中趋势统计数据和离差测量值。(如果您需要提醒,请参见集中趋势和分散程度之间的差异。)
该函数的一个主要优点是它接受单个矢量和数据帧。如果提供了数据框,则会忽略所有非数字列,因此您不必在运行函数之前亲自移除它们。
descr()
功能允许显示:
- 只有一个你选择的描述性统计的选择,以平均值和标准差的
stats = c("mean", "sd")
参数为例 - 使用
stats = "fivenum"
的最小值、第一个四分位数、中间值、第三个四分位数和最大值 - 最常见的描述性统计(均值、标准差、最小值、中值、最大值、有效观察值的数量和百分比),用
stats = "common"
:
descr(dat,
headings = FALSE, # remove headings
stats = "common") # most common descriptive statistics##
## Petal.Length Petal.Width Sepal.Length Sepal.Width
## --------------- -------------- ------------- -------------- -------------
## Mean 3.76 1.20 5.84 3.06
## Std.Dev 1.77 0.76 0.83 0.44
## Min 1.00 0.10 4.30 2.00
## Median 4.35 1.30 5.80 3.00
## Max 6.90 2.50 7.90 4.40
## N.Valid 150.00 150.00 150.00 150.00
## Pct.Valid 100.00 100.00 100.00 100.00
提示:如果你有大量的变量,添加transpose = TRUE
参数以获得更好的显示效果。
为了按组计算这些描述性统计数据(例如,我们数据集中的Species
),请将descr()
函数与stby()
函数结合使用:
stby(data = dat,
INDICES = dat$Species, # by Species
FUN = descr, # descriptive statistics
stats = "common") # most common descr. stats## Descriptive Statistics
## dat
## Group: Species = setosa
## N: 50
##
## Petal.Length Petal.Width Sepal.Length Sepal.Width
## --------------- -------------- ------------- -------------- -------------
## Mean 1.46 0.25 5.01 3.43
## Std.Dev 0.17 0.11 0.35 0.38
## Min 1.00 0.10 4.30 2.30
## Median 1.50 0.20 5.00 3.40
## Max 1.90 0.60 5.80 4.40
## N.Valid 50.00 50.00 50.00 50.00
## Pct.Valid 100.00 100.00 100.00 100.00
##
## Group: Species = versicolor
## N: 50
##
## Petal.Length Petal.Width Sepal.Length Sepal.Width
## --------------- -------------- ------------- -------------- -------------
## Mean 4.26 1.33 5.94 2.77
## Std.Dev 0.47 0.20 0.52 0.31
## Min 3.00 1.00 4.90 2.00
## Median 4.35 1.30 5.90 2.80
## Max 5.10 1.80 7.00 3.40
## N.Valid 50.00 50.00 50.00 50.00
## Pct.Valid 100.00 100.00 100.00 100.00
##
## Group: Species = virginica
## N: 50
##
## Petal.Length Petal.Width Sepal.Length Sepal.Width
## --------------- -------------- ------------- -------------- -------------
## Mean 5.55 2.03 6.59 2.97
## Std.Dev 0.55 0.27 0.64 0.32
## Min 4.50 1.40 4.90 2.20
## Median 5.55 2.00 6.50 3.00
## Max 6.90 2.50 7.90 3.80
## N.Valid 50.00 50.00 50.00 50.00
## Pct.Valid 100.00 100.00 100.00 100.00
使用dfSummary()
的数据帧摘要
dfSummary()
函数生成一个汇总表,其中包含数据集中所有变量的统计数据、频率和图表。显示的信息取决于变量的类型(字符、因子、数字、日期),也根据不同值的数量而变化。
dfSummary(dat)## Data Frame Summary
## dat
## Dimensions: 150 x 6
## Duplicates: 1
##
## ----------------------------------------------------------------------------------------------------------------------
## No Variable Stats / Values Freqs (% of Valid) Graph Valid Missing
## ---- --------------- ------------------------ -------------------- -------------------------------- -------- ---------
## 1 Sepal.Length Mean (sd) : 5.8 (0.8) 35 distinct values . . : : 150 0
## [numeric] min < med < max: : : : : (100%) (0%)
## 4.3 < 5.8 < 7.9 : : : : :
## IQR (CV) : 1.3 (0.1) : : : : :
## : : : : : : : :
##
## 2 Sepal.Width Mean (sd) : 3.1 (0.4) 23 distinct values : 150 0
## [numeric] min < med < max: : (100%) (0%)
## 2 < 3 < 4.4 . :
## IQR (CV) : 0.5 (0.1) : : : :
## . . : : : : : :
##
## 3 Petal.Length Mean (sd) : 3.8 (1.8) 43 distinct values : 150 0
## [numeric] min < med < max: : . : (100%) (0%)
## 1 < 4.3 < 6.9 : : : .
## IQR (CV) : 3.5 (0.5) : : : : : .
## : : . : : : : : .
##
## 4 Petal.Width Mean (sd) : 1.2 (0.8) 22 distinct values : 150 0
## [numeric] min < med < max: : (100%) (0%)
## 0.1 < 1.3 < 2.5 : . . :
## IQR (CV) : 1.5 (0.6) : : : : .
## : : : : : . : : :
##
## 5 Species 1\. setosa 50 (33.3%) IIIIII 150 0
## [factor] 2\. versicolor 50 (33.3%) IIIIII (100%) (0%)
## 3\. virginica 50 (33.3%) IIIIII
##
## 6 size 1\. big 77 (51.3%) IIIIIIIIII 150 0
## [character] 2\. small 73 (48.7%) IIIIIIIII (100%) (0%)
## ----------------------------------------------------------------------------------------------------------------------
describeBy()
来自{psych}
包
{psych}
包中的describeBy()
函数允许按分组变量报告多个汇总统计数据(即有效病例数、平均值、标准偏差、中值、修整平均值、mad:中值绝对偏差(与中值的偏差)、最小值、最大值、范围、偏斜度和峰度)。
library(psych)
describeBy(dat,
dat$Species) # grouping variable##
## Descriptive statistics by group
## group: setosa
## vars n mean sd median trimmed mad min max range skew kurtosis
## Sepal.Length 1 50 5.01 0.35 5.0 5.00 0.30 4.3 5.8 1.5 0.11 -0.45
## Sepal.Width 2 50 3.43 0.38 3.4 3.42 0.37 2.3 4.4 2.1 0.04 0.60
## Petal.Length 3 50 1.46 0.17 1.5 1.46 0.15 1.0 1.9 0.9 0.10 0.65
## Petal.Width 4 50 0.25 0.11 0.2 0.24 0.00 0.1 0.6 0.5 1.18 1.26
## Species* 5 50 1.00 0.00 1.0 1.00 0.00 1.0 1.0 0.0 NaN NaN
## size* 6 50 NaN NA NA NaN NA Inf -Inf -Inf NA NA
## se
## Sepal.Length 0.05
## Sepal.Width 0.05
## Petal.Length 0.02
## Petal.Width 0.01
## Species* 0.00
## size* NA
## ------------------------------------------------------------
## group: versicolor
## vars n mean sd median trimmed mad min max range skew
## Sepal.Length 1 50 5.94 0.52 5.90 5.94 0.52 4.9 7.0 2.1 0.10
## Sepal.Width 2 50 2.77 0.31 2.80 2.78 0.30 2.0 3.4 1.4 -0.34
## Petal.Length 3 50 4.26 0.47 4.35 4.29 0.52 3.0 5.1 2.1 -0.57
## Petal.Width 4 50 1.33 0.20 1.30 1.32 0.22 1.0 1.8 0.8 -0.03
## Species* 5 50 2.00 0.00 2.00 2.00 0.00 2.0 2.0 0.0 NaN
## size* 6 50 NaN NA NA NaN NA Inf -Inf -Inf NA
## kurtosis se
## Sepal.Length -0.69 0.07
## Sepal.Width -0.55 0.04
## Petal.Length -0.19 0.07
## Petal.Width -0.59 0.03
## Species* NaN 0.00
## size* NA NA
## ------------------------------------------------------------
## group: virginica
## vars n mean sd median trimmed mad min max range skew
## Sepal.Length 1 50 6.59 0.64 6.50 6.57 0.59 4.9 7.9 3.0 0.11
## Sepal.Width 2 50 2.97 0.32 3.00 2.96 0.30 2.2 3.8 1.6 0.34
## Petal.Length 3 50 5.55 0.55 5.55 5.51 0.67 4.5 6.9 2.4 0.52
## Petal.Width 4 50 2.03 0.27 2.00 2.03 0.30 1.4 2.5 1.1 -0.12
## Species* 5 50 3.00 0.00 3.00 3.00 0.00 3.0 3.0 0.0 NaN
## size* 6 50 NaN NA NA NaN NA Inf -Inf -Inf NA
## kurtosis se
## Sepal.Length -0.20 0.09
## Sepal.Width 0.38 0.05
## Petal.Length -0.37 0.08
## Petal.Width -0.75 0.04
## Species* NaN 0.00
## size* NA NA
aggregate()
功能
aggregate()
函数允许将数据分成子集,然后计算每个子集的汇总统计数据。例如,如果我们想通过Species
和Size
计算变量Sepal.Length
和Sepal.Width
的平均值:
aggregate(cbind(Sepal.Length, Sepal.Width) ~ Species + size,
data = dat,
mean)## Species size Sepal.Length Sepal.Width
## 1 setosa big 5.800000 4.000000
## 2 versicolor big 6.282759 2.868966
## 3 virginica big 6.663830 2.997872
## 4 setosa small 4.989796 3.416327
## 5 versicolor small 5.457143 2.633333
## 6 virginica small 5.400000 2.600000
感谢阅读。我希望这篇文章能帮助你在 r 中做描述性统计。如果你想手工做同样的事情或理解这些统计代表什么,我邀请你阅读文章"手工描述性统计"。
和往常一样,如果您有与本文主题相关的问题或建议,请将其添加为评论,以便其他读者可以从讨论中受益。
相关文章:
- 安装和加载 R 包的有效方法
- 我的数据符合正态分布吗?关于最广泛使用的分布以及如何检验 R 中的正态性的注释
- R 中的 Fisher 精确检验:小样本的独立性检验
- R 中独立性的卡方检验
- 如何在简历中创建时间线
- 正态性检验,如夏皮罗-维尔克或科尔莫戈罗夫-斯米尔诺夫检验,也可以用来检验数据是否遵循正态分布。然而,在实践中,正态性检验通常被认为过于保守,因为对于大样本量,与正态性的微小偏差都可能导致违反正态性条件。由于这个原因,通常情况下,正态性条件是基于视觉检查(直方图和 QQ 图)和形式检验(例如夏皮罗-维尔克检验)的组合来验证的。 ↩
- 注意这个包需要
plain.ascii
和style
参数。在我们的例子中,这些参数被添加到每个块的设置中,所以它们是不可见的。 ↩ - 注意,也可以计算比值比和风险比。关于这个问题的更多信息,请参见软件包的简介,因为这些比率超出了本文的范围。 ↩
原载于 2020 年 1 月 22 日 https://statsandr.com*。*