React.js —基本挂钩(使用状态、使用效果和使用上下文)
原文:https://towardsdatascience.com/react-js-basic-hooks-usestate-useeffect-usecontext-1ed82a799db2
对 React.js 三个基本的、也是最重要的钩子的简单介绍
埃菲社在 Unsplash 上拍摄的照片
前言——从本文的标题可以推断出,这并不是一个全面的指南,介绍 React.js 新版本中可以使用的所有 钩子,而是对大多数使用 React.js 的人最有可能遇到的 基本 钩子的概述。如果您对可以“挂钩”到 React.js 组件中的所有挂钩的完整列表感兴趣(截至本文发布时的版本 16.13.1),请参考 React.js 文档https://reactjs.org/docs/hooks-reference.html*。此外,如果你不熟悉 React.js,我可以建议你看看 YouTube 上的*Ben Awad的 实用 React 系列,作为你的教育努力的入门,因为本文假设你事先了解 React.js。话不多说,我希望你喜欢它!
React.js 钩子是什么?
要直接引用 React.js 文档,
钩子是 React 16.8 中的新增功能。它们让你不用写类就可以使用状态和其他 React 特性…钩子是一个特殊的函数,它让你“挂钩”React 特性。
传统上,在 React.js 中,由于 React.js 作为 UI 库与 JavaScript 交互的性质,需要基于类的组件来利用生命周期方法和状态。不过在 React.js 的新版本中,使用 React.js 在前端工作的程序员现在可以创建带有钩子的功能组件 ,,它们与基于类的组件相似,但是:
- 更容易测试 —尽管测试在个人项目中的影响不如在高度可用、公开部署的应用程序中的影响大,但它是任何组织的软件开发生命周期的重要部分。无论这是以持续集成/持续开发服务的形式出现,如 CircleCI 或 TravisCI ,还是直接通过库测试个性化功能,如 Jest 或 Selenium ,钩子使得测试 React.js 组件比测试基于类的组件容易得多。
- 更容易阅读——这对有许多团队成员的项目特别有帮助。尽管人们可能会假设团队在如何构建 UI 方面可以遵循特定的设计语言、方法和样式指南,但是去除基于类的组件架构的脂肪允许工程师重新编写位于 JavaScript 语言核心的程序,而不是抽象出不必要的东西。
- 更接近传统 JavaScript——如上所述,编写功能组件减少了用 JavaScript 编写“特殊函数”(类)的工作量。这并不是说用 JavaScript 编写基于类的组件是一件坏事,因为 JavaScript 由于其多范例方法在技术上可以用作面向对象的语言,但这是因为 JavaScript 如何处理类以及从这种类声明中实例化的对象。这就是 JavaScript 的基本模式和原则发挥作用的地方,比如原型链和继承,虽然不在本文的讨论范围之内,但它们是 JavaScript 非常重要的特性。另一件事是,如果您使用基于类的组件,您总是必须从内置的 React.js 类扩展,而功能组件是纯 JavaScript 函数,其中 JSX 和钩子由 React.js 通过导入来识别。
- 最佳实践 —软件工程师必须接受的一个事实是,他们今天在自己运营和/或工作的公司中利用的技术,在不远的将来很可能会变得自满,这是好事,还是坏事,取决于你的个人哲学。当然,也有例外:20 世纪 70 年代的 SQL,80 年代的 C++和 Python,但是随着技术的进步和抽象层覆盖在我们今天使用的工具上,更新的 API 出现了,为了构建一个产品,必须在思想上消化并在物质上实现这些 API。没有一个单独的实体掌握着未来技术是什么的答案,但是你可以通过观察谁在构建未来来获得一个很好的想法,比如 React.js 的脸书
- 代码密集程度较低——这也可以归入“更容易阅读”的范畴,但我的直觉告诉我,基于我之前提到的软件工程中的一个范例,将它划分为自己的要点;抽象。虽然你通常必须用一些性能来换取抽象的实现,但它通常是产品发展的氧气。在我们的案例代码中,将曾经复杂、功能稀疏、在许多实例中重复的东西转化为更容易实现的解决方案,提供整体更好的工程体验,并产生与以前的迭代相同(如果不是更好的话)的方法,这是任何产品生命周期背后的主题。React.js 也不例外。
随着本文的进展,我将讨论三个“基本的”React.js 挂钩,作为构建 UI 的工程师,您很可能会遇到这三个挂钩。它们是:
- 使用状态()
*它声明了一个“状态变量…这是一种在函数调用之间“保留”一些值的方法——*
useState
是一种新的方法,可以使用*this.state*
在类中提供的完全相同的功能。通常,当函数退出时,变量“消失”,但是状态变量被 React 保留。
- useEffect()
如果你熟悉 React 类的生命周期方法,你可以把
*useEffect*
钩子想象成*componentDidMount*
*componentDidUpdate*
,以及*componentWillUnmount*
的组合。**
- 使用上下文()
在典型的 React 应用程序中,数据是通过 props 自顶向下(从父到子)传递的,但是对于应用程序中许多组件需要的某些类型的 props(例如,区域设置首选项、UI 主题)来说,这种用法可能很麻烦。上下文提供了一种在组件之间共享这些值的方法,而不必显式地在树的每一层传递一个属性。
入门指南
使用状态
useState(),如上所述,将状态挂钩到组件中。很容易整合到功能组件中:
useState()的示例
在上面的截图中,我们可以注意到一些事情。useState()有两个常量,您应该在调用 useState()钩子时认识到:
- 状态数据结构/类型本身(例如—
state
)将保存该状态实例的初始值。您只能向 useState()传递一个参数,但是您可以多次调用 useState(),您将在阅读材料中看到进一步的概述。 - 在组件生命周期的特定点执行的 setter 函数(例如—
setState
),用于更新状态数据结构/类型的值。
在 React.js 文档中,关于上面例子中看到的数组析构,还有一点需要注意:
当我们用
*useState*
声明一个状态变量时,它会返回一个 pair——一个包含两项的数组。第一项是当前值,第二项是让我们更新它的函数。使用*[0]*
和*[1]*
来访问它们有点令人困惑,因为它们有特定的含义。这就是我们使用数组析构的原因。
需要注意的一点是,你可以随意调用这两个变量,[state, setState]
。您会遇到两种常见的命名约定,大多数工程师都会使用:
- 将所有内容封装在一个 JavaScript 对象中,并将所有状态属性分配给各自的默认值。如果你以前使用过基于类的 React.js 组件,你会非常熟悉。这两个常量被命名为
state
和setState
是很常见的,但是同样,您可以随意命名它们。在管理状态时,我给这种方法起了一个绰号,我将在本文的其余部分提到它,它就是“垂直缩放的组件状态”。示例:
**const [state, setState] = useState({
loading: true,
error: '',
signUpFormFields: {
username: '',
password: ''
},
scoreCount: 0
});**
- 将析构数组中的第一个常量命名为组件状态中需要跟踪和更新的部分的大小写形式(例如,——
loading, error, isAuthenticated, signupFormFields, etc.
)。然后,将第二个常量命名为与第一个相同的名称,但是在命名约定前加上 set- ,并适当调整大小写以遵循骆驼大小写的惯例。在管理状态时,我给这种方法起了一个绰号,我将在本文的其余部分引用它,它就是“水平缩放的组件状态”。示例:
**const [loading, setLoading] = useState(true); // boolean
const [error, setError] = useState(''); // string
const [signUpFormFields, setSignUpFormFields] = useState({
username: '',
password: '' ,
}); // object
const [value, setValue] = useState(0); // int**
两者做的事情完全一样(大部分情况下),尽管它们都有各自的优缺点:
- 当谈到水平缩放状态时,一个负面的影响是您会发现自己必须运行更多的 setter 函数调用(例如—
setState()
)才能将组件的状态更新到期望的结果。确实存在减轻这种情况发生的解决方案,比如使用 useReducer()钩子,但是这超出了本文的范围。如果你正在寻找实现这个解决方案的教程,我强烈推荐 YouTube 上的 Harry Wolff 的视频,他在深入使用 useReducer()钩子方面做了令人难以置信的工作。当您使用异步调用、处理承诺或者使用像 fetch 或 axios 这样的库对可能直接影响组件状态的 API 执行 CRUD 操作时,水平缩放组件状态的一个好处是。这样做的原因与您将从这些服务中检索多少数据有关,重写或复制整个垂直缩放的状态对象的成本将远远高于只对应用程序逻辑的这一部分进行一次互斥的 useState()调用的成本。 - 垂直缩放状态的好处是,无论何时想要更新 JavaScript 对象,只需调用一个 setter 函数。但是,有些缺点是,除非您更新 JavaScript 对象中的每个属性,否则您将不得不利用 JavaScript 的扩展语法,以便不会将更改的状态值重置回它们在 useState()钩子中定义的原始声明。这个问题也存在于水平缩放的组件状态中,但是扩展操作的成本通常要低得多,因为您通常只处理非常小的更改。如果这听起来有点混乱,如果我拿上面的垂直缩放的例子并想更新
loading: false
的话,下面是你必须做的事情:
**const [state, setState] = useState({
loading: true,
error: '',
signUpFormFields: {
username: '',
password: ''
},
scoreCount: 0
});setState({ error: 'A new error entry!' });/*
If you wanted 'error' to persist its newly set state value ('A new error entry!'), you will have to use the spread operator the next time you updated your state (as I do below).Something to take note of: We don't have to use the spread operator in the first setState() invocation update above because there haven't been any previous changes to the initial state values.
*/setState({ ...state, loading: false });/*
Your updated state object would now look like this:{
loading: false,
error: 'A new error entry!',
signUpFormFields: {
username: '',
password: ''
},
scoreCount: 0
}*/**
如果您正在寻找一个关于应该水平还是垂直缩放状态的最佳实践,React.js 文档建议使用多个状态调用来代替单个状态(即,使用水平缩放的组件状态):
然而, 我们建议将状态拆分成多个状态变量,基于这些变量的值往往会一起变化。
这背后的原因主要与 React.js 组件的记忆和它们处理的状态有关。这基本上是一种奇特的说法,即您应该通过缓存一次初始调用来优化高成本函数调用的速度,然后只从缓存中提取已更改的数据。这种方法意味着您很可能不得不加入更复杂的 React.js 挂钩,比如 useMemo() 和/或 useCallback() ,这超出了本文的范围。如果你正在寻找学习如何利用这些更复杂的 React.js 钩子的资源,我会推荐 YouTube 上的 Ben Awad 的 useMemo 和 useCallback 教程。
使用效果
如前所述,useEffect()通过一次函数调用将主要概念componentDidMount()
、componentDidUpdate()
和componentWillUnmount()
挂钩到一个组件中。很容易整合到功能组件中:
useEffect()挂钩
在上面的截图中,我们可以注意到一些事情。有两个参数传递给 useEffect():
- 一个匿名的回调函数,存放你的 useEffect 逻辑。这个逻辑的执行基于如何设置 useEffect()来运行(我们将在下面进一步讨论)。
- 第二个是一个数组,接受逗号分隔的变量,称为依赖列表。这就是改变 useEffect()操作方式的方法。
重要提示:如果不传递 useEffect()钩子中的第二个参数(即依赖列表),那么钩子将在每个渲染上运行——如果/当你将这个钩子与 useState()之类的东西结合使用时,这可能会有问题,因为你的组件可能会陷入一个重新渲染循环,其中;首先,组件在初始渲染时运行 useEffect()钩子。然后,useState()中的一些数据通过上面在 useEffect()钩子中描述的 setter 函数进行更新。更新后,组件会因为状态更新而重新呈现,并再次执行相同的 useEffect()钩子。我将在下面讨论如何使用 useEffect()来防止这种情况。
尽管 useEffect()确实利用了componentDidMount()
、componentDidUpdate()
和componentWillUnmount()
的各个方面,但是最好不要把 useEffect()看作是一个钩子,它复制了从这三个生命周期方法中的每一个获得的功能,并将它们压缩成一个可以跨组件内的许多实例调用的函数。
相反,可以将 useEffect()看作是一个函数,它在渲染之前(通常称为“清理”)、渲染之后以及组件卸载之前为组件执行特定的任务。
由于 useEffect()可以以多种不同的方式使用,其中大部分我不会在本文的范围内讨论(不必担心,我将在下面提供更多关于钩子的边缘情况的参考资料),如果我对这个特定的 React.js 特性使用 Pareto 原则,我将只讨论 useEffect()实现 80%时间的 20%的方式。
下面是实现 useEffect()挂钩的更常见的方法:
- 要使 useEffect()调用仅在每次装载和卸载时运行,请按以下方式使用 useEffect()挂钩:
**useEffect(() => {
// some component logic to execute...
}, []);/*
Notice the empty array as the second argument above. We don't pass anything to the array as we don't was useEffect() to depend on anything - thus the purpose of having the dependency list in the first place.
*/**
- 为了使 useEffect()调用运行得更少或更多,通常基于该 useEffect()调用依赖于什么(即,传递给依赖列表的内容),以下列方式使用 useEffect()挂钩:
**const [value, setValue] = useState(0);useEffect(() => {
// some component logic to execute...
}, [value, setValue]);/*
Notice the dependency array as the second argument above. We pass 'value' to the array as an example to showcase how this hook can work. This useEffect() invocation will execute every single time 'value' is updated.Another thing to mention is that arguments in the dependency list don't have to come from other hooks like they do in this example - they can be other forms of data that are assigned to a particular variable where the underlying assigned values can be/are mutated.
*/**
就像 useState()一样,您可以根据组件的需要使用任意多个 useEffect()实例。
钩子让我们根据代码正在做什么来分割代码 而不是一个生命周期方法名。React 将按照指定的顺序应用组件使用的每个效果。
如果您需要对上面提供的两个示例中没有涉及的 DOM 进行定制修改,我建议您查阅 React.js 文档,主要关注部分是“效果的定时”。
如果你正在寻找一个关于 useEffect()钩子的全封装资源,可以解决你在这个钩子上遇到的所有问题,那么有一篇文章我会推荐你去读,就像读 React.js 福音一样:use effect完全指南。由 Redux、Create React 应用程序的合著者丹·阿布拉莫夫撰写,他是一位全能的不可思议的工程师,作者深入研究了这种特殊挂钩必须提供的细微差别和功能,同时提供了易于遵循的示例。我无法推荐比这篇文章更好的关于这个钩子的资源和人了。
使用上下文
前言—在发表本文时,还有另一个针对全局状态管理的 React.js 解决方案,目前正处于开发的试验阶段。这个包被称为 反冲 似乎是 React.js 上下文 API 之类的东西和Redux或MobX之类的不可知全局状态管理工具之间的交叉,同时为如何管理前端的全局状态带来了新的设计模式。我不会在本文的范围内讨论这个库,但是我觉得有必要提一下,因为该产品将来可能会发展到成为实现 React.js 前端全局状态管理解决方案的最佳实践。
React Context API 可能是 16.8 更新中我最喜欢的新增功能之一,它是一套 API 特性,提供了一个可变的全局状态数据结构,可以在整个组件树中的任何点上挂钩到组件,从而避免了 React.js 反模式(称为 prop drilling)。
以下面的 React.js 组件树架构为例:
React.js 组件树示例
假设您在 Authenticated 文件夹(components/Authenticated)中的index.jsx
文件中有一些状态逻辑,并且您想将这些数据传递给 Section3 组件(components/Authenticated/Dashboard/Body/section 3)。在 React Context API 之前,您必须用 props“钻取”每个中介组件,即使您在到达理想的祖先组件之前不打算在该组件中使用该 prop 数据。这可能不是沿组件树向下传递数据的最合适的解决方案,原因有几个,Kent C. Dodds 关于这个主题的博客文章完美地总结了所有这些原因:
It [prop drilling]不可避免地会给你的应用程序带来一个非常混乱的数据模型。任何人都很难找到数据在哪里被初始化,在哪里被更新,在哪里被使用。
回答“我可以在不破坏任何东西的情况下修改/删除这段代码吗?”在那样的世界里是很难回答的。这是你在编码时应该优化的问题。
但是随着应用程序的增长,您可能会发现自己要钻透许多层的组件。当你最初写出来的时候,这通常没什么大不了的,但是在代码已经工作了几个星期之后,对于一些用例来说,事情开始变得难以处理:
-重构一些数据的形状(即:
*{user: {name: 'Joe West'}}*
->*{user: {firstName: 'Joe', lastName: 'West'}}*
)**-由于(重新)移动了一个需要一些道具但不再需要的组件,导致过度转发道具(传递了多余的道具)。
-欠转道具+滥用
*defaultProps*
这样你就不会意识到道具丢失了(也是因为(重新)移动了一个组件)。——中途重命名道具(即
*<Toggle on={on} />*
渲染*<Switch toggleIsOn={on} />*
)让你的大脑很难记住这些。还有各种各样的其他情况,特别是在重构过程中,道具演练会带来一些真正的痛苦。
这就是 React 的上下文 API 试图缓解出现的任何问题的地方。您可以将 React 的 Context API 视为与 Redux 或 MobX 等众所周知的全局状态管理工具类似的解决方案,但是开销和样板文件要少得多,并且 React.js 的方法更接近于 React . js,而不是与您使用的前端库/框架无关的工具。
当然,这两种解决方案各有利弊:
- 例如,如果应用程序中的状态不断变化,或者如果您可以采用更大的包大小,因为您知道您的产品将从更全面的全局状态管理套件中受益更多,那么 Redux 或 MobX 比 React Context API 更有意义。因为一个语境。Provider 的行为基本上类似于一个数据类型/结构(即垂直缩放的状态),可以在组件树中的任何位置访问,如果您在一次 Context.Provider 调用中存储大量不断更新值的属性,更新也会变得非常昂贵
- 另一方面,如果您的应用程序较小,只需要很少与全局状态交互,或者如果您使用多次上下文调用。Provider(不是最好的解决方案——您应该只使用 React 的上下文 API,如下所述)来存储全局状态的不同部分,那么使用上下文 API 可能比 Redux 或 MobX 更有意义。
当不同嵌套层次的许多组件需要访问某些数据时,主要使用上下文。请谨慎使用它,因为它使组件重用更加困难。
如果只是想避免一些道具通过很多关, 组件构成 往往是比上下文更简单的解决方案。
既然我们已经介绍了 React.js 上下文 API 的一般概述、它解决的问题以及它的“竞争”,下面是一个使用上下文 API 时提供者/消费者关系的示例,以及如何处理更新:
src/providers/some provider/index . jsx
src/index.js
上面的 useEffect()钩子+ SomeContext 更新
上面的照片提供了一个如何使用上下文的例子。提供程序与函数 createContext()和钩子 useContext()并行。如果这些照片有点难以理解,你可以用一种潜在的更容易理解的方式来思考:
通过 React.js 中的createContext()
函数创建一个上下文对象(第一张照片中的第 3 行)。这个对象总是带有一个提供者组件(从第一张照片的第 5 行中的对象析构可以看出)和一个消费者组件。提供者允许使用者订阅对上下文对象的更改。提供者组件有一个名为value
(第一张照片中的第 14 行)的属性,它接收提供者希望传递给后代消费者组件的数据。在上面的例子中,我们将state
和setState
变量从提供者的父组件中的 useState()钩子向下传递到任何消费者组件,这样可以发生两件事;首先,消费者组件可以通过state
访问最初存在于提供者的父组件状态中的所有值。第二,通过我们传递的setState
setter 函数,他们能够在封装组件树中的任何点改变state
数据(我正在改变第三张照片中的数据,如第 14-18 行所示)。
然后,提供者可以在组件树中以任何级别多次包装任何组件;在第二张照片中,您可以看到我将提供者组件包装在整个应用程序中。这将提供者和它可以向下传递以供消费的数据放在组件树的顶部。如果我想嵌套 Provider 组件,我也可以这样做。理论上,对于不能将一个提供者组件包装在一个消费者组件上没有限制——您甚至可以将多个提供者包装在一起,并允许消费者组件消费多个嵌套上下文(有关这方面的更多信息,请参见 React.js 文档中的’消费多个上下文)。
你可能会注意到,我没有使用上下文。用于处理消费者的消费者财产。你只需要使用上下文。如果您想访问上下文对象本身,而不是 Context.Provider,请使用 Context 对象的 Consumer 属性。通过value
支柱传递state
和setState
的提供者。这样,我们不仅可以访问状态,还可以优雅地更新状态。如果你只是想让你的上下文对象被消费而不是变异,你可以把 useState()钩子中的所有东西直接放到 createContext()函数中,而不是放在null
中。
如果您需要更多地了解 React.js 上下文 API,我强烈推荐几个视频资源:
总而言之,与功能组件并行使用的基本 React.js 钩子提供了一种惊人的、直观的、完全令人愉快的方式来用 JavaScript 构建通用的、可伸缩的 UI。理解这三个基本的钩子将为更复杂的,甚至是定制的钩子以及它们的功能打下坚实的基础,当你在未来的工作中学习和使用它们的时候。
如果你发现了任何错误,请留下评论,我会尽快回复你。
感谢您的时间和关注。
使用 Python 中的 PyMuPDF 读取多列 PDF
原文:https://towardsdatascience.com/read-a-multi-column-pdf-using-pymupdf-in-python-4b48972f82dc
NLP 工具
逐步介绍 OCR 的奇妙世界(附图片)
照片由 Jaizer Capangpangan 在 Unsplash 拍摄
OCR 或光学字符识别是用于从图像或文档中自动提取文本的技术。在这些图像和文档中找到的文本可以是任何键入的、手写的、显示在某种屏幕上的,或者可能是以上所有的!
OCR 最初用作数据输入的高级形式,最常用于将纸质文物中的文本数字化,“以便它们可以被电子编辑、搜索、更紧凑地存储、在线显示,并用于机器处理,如认知计算、机器翻译、(提取的)文本到语音、关键数据和文本挖掘”[1]。例如,在我的工作中,我依靠 OCR 技术将非结构化和半结构化数据转换成足够清晰的格式,以便放入电子表格或数据库中。
OCR 研究包括模式识别、人工智能和计算机视觉。[1]虽然有许多商业产品提供 OCR 服务,如 Amazon Textract 和 ABBYY,但没有什么比用我们最喜欢的编程语言 Python 编写的免费解决方案更好的了。
今天,我们将与 PyMuPDF(也称为 fitz)一起尝试 OCR。
快点,我们跳进去吧!
装置
在我们启动 jupyter 笔记本之前,让我们确保我们的机器上已经安装了软件包。要安装 pyMuPDF,请在终端中键入以下内容:
pip install pymupdf
然后,让我们启动一个 jupyter 笔记本,键入以下内容来导入 PyMuPDF 包。
接下来,让我们设置全局变量。请确保将“replace _ this _ with _ your _ own _ path _ to _ file”替换为您的 pdf 文件的路径,或者您可以下载我在这里使用的示例文件。
数字化 pdf 文件是可搜索的 pdf 文件。要确定 pdf 是否可搜索,请打开一个 pdf 文档,按下CTRL+F
并键入文档中出现的单词。如果程序能找到这个词,它就是可搜索的。否则,它可能是一个扫描的 pdf。正如我们将在后面看到的, pymupdf 不支持扫描的 pdf 。
可搜索(数字化)pdf 文档的示例。作者截图。
不可搜索(扫描)的 pdf 文档示例。作者截图。
文本提取:“文本”
使用 PyMuPDF 从可搜索的 pdf 中提取文本非常容易。在 jupyter 笔记本的单元格块中键入以下内容,然后观看神奇的事情发生:
我们使用with fitz.open(DIGITIZED_FILE) as doc:
,这样我们就不用担心用close()
关闭文件。接下来,如果有多个页面,我们使用一个for
循环遍历 pdf 文档中的所有页面。文本的实际提取发生在代码块的核心部分,text = page.get_text()
。在我们的例子中,我们使用默认参数(“text”)。下面是print(text)
的结果:
作者截图。
如果我们使用text = page.get_text("text")
,也会得到相同的结果。其他选择包括“块”和“词”有关 get_text 方法选项的完整列表,请访问文档。[2]
page.get_text()参数。作者截图。
有趣的是,pyMuPDF 成功地以自然阅读顺序阅读了文档,即使有两列。
PyMuPDF 从左到右阅读文档。作者截图。
PyMuPDF 无缝地读取这两列。作者截图。
文本提取:“块”
默认参数输出一个字符串。但是,其他参数返回不同的格式,如列表和元组。例如,page.get_text("blocks")
返回元组的列表。让我们试一试:
上面,我们在page.get_text()
里面加了"blocks"
。结果如下:
作者截图。
根据文档,的输出是:
作者截图。
其中前四项是边界框坐标,第五项是文本本身(用换行符或“\n”分隔多行),第六项是块号,第七项是块类型(0 表示文本,1 表示图像)。
作者截图。
如果你需要知道页面上的什么地方,额外的信息是有益的。例如,如果您需要绘制边界框或高亮显示特定的块。
作者截图。
文本提取:“单词”
让我们试试“单词”参数,只是为了好玩:
结果显示,参数“words”返回一个元组的列表,该列表逐字表示文本。
作者截图。
单词“sit”被标注为该页第一块的第三行中的第二个单词。作者截图。
严重失败
如前所述, pymupdf 不适用于扫描的 pdf 。
作者截图。
别担心!有一个包可以解决这个问题…
结论
今天,我们看到了 PyMuPDF 的行动。这是一个有价值的工具。我已经用它自动搜索了数百个 pdf 文档,而不用打开 Adobe Acrobat。
可能性是无限的。
但是扫描的 pdf 呢?!请继续关注我的下一篇文章,内容包括如何使用 pytesseracT3 阅读多列 PDF。
谢谢你过来看我的帖子。希望 PyMuPDF 能像帮助我一样帮助你!你可以在 Github 上这里找到我用的笔记本。
敬请期待!
如果你想了解更多关于我从懒鬼到数据科学家的旅程,请查看下面的文章:
如果你正在考虑改变方向,进入数据科学领域,现在就开始考虑重塑品牌:
参考
[1]https://en.wikipedia.org/wiki/Optical_character_recognition
[2]https://pymupdf.readthedocs.io/en/latest/textpage.html?highlight=get_text#
使用 Python 中的 Pytesseract 阅读多列 PDF
原文:https://towardsdatascience.com/read-a-multi-column-pdf-with-pytesseract-in-python-1d99015f887a
NLP 工具
逐步介绍 OCR 的奇妙世界(附图片)
由 Jaizer Capangpangan 在 Unsplash 上拍摄的照片
在前一篇文章中,我们学习了如何使用 PyMuPDF 包阅读 PDF 文档。我们还了解到,这种方法只有在 PDF 被数字化或可搜索的情况下才有效。否则,如果 PDF 被扫描并且不可搜索,PyMuPDF 就不起作用。
宇宙魔方来救援了。
Pytesseract 是另一个 OCR(光学字符识别)工具,作为 Google 的 Tesseract-OCR 引擎的 Python 包装器。它可以“识别”和“阅读”嵌入图像中的文本。”[1]
Tesseract-OCR 安装
在开始编码之前,我们首先必须安装 Tesseract-OCR,并将其安装路径添加到环境变量中。
对于 Windows,我们可以直接去这里下载安装程序。对于其他操作系统,在此进入。
让我们开始安装吧!
作者截图
作者截图
作者截图
作者截图
这一次,我没有使用默认选项,而是推荐下载额外的数据,这样您以后就不必再这样做了。勾选下面圈出的方框:
作者截图
作者截图
在下一个屏幕中,请注意将安装 Tesseract-OCR 的目标文件夹。当我们将它添加到环境变量的路径中时,我们将需要它。
作者截图
在下一个屏幕上单击“安装”。
作者截图
安装完成后,单击“完成”。
作者截图
此时,如果我们打开一个终端并键入tesseract --version
,我们将看到一个错误。
作者截图
我们首先需要做的是将 Tesseract-OCR 安装文件夹添加到路径中。为此,按下 Windows 键并立即键入env
。然后会出现一个搜索框,显示几个选项供你考虑。让我们选择“为您的帐户编辑环境变量”
作者截图
在下一个屏幕上,单击路径和“编辑…”按钮。
作者截图
单击下一个屏幕顶部的“新建”按钮。
作者截图
现在,让我们进入 Tesseract-OCR 安装文件夹。单击“确定”
作者截图
在下一个屏幕上再次单击“确定”。
作者截图
当我们在终端中输入tesseract --version
时,我们将得到关于宇宙魔方的信息,而不是一个错误。
作者截图
既然已经安装了 Tesseract-OCR 并且更新了我们的路径,我们就可以开始通过 pip 安装我们需要的 Python 包了。
使用 PIP 安装
因此,回到终端并键入以下内容:
pip install pytesseract
作者截图
完成后,键入以下内容:
pip install opencv-contrib-python
作者截图
现在我们已经准备好启动 jupyter 笔记本了!
Jupyter 笔记本
打开笔记本,让我们导入一些包:
现在,让我们也设置一个全局变量:
现在,让我们来看看我们的输入 pdf:
如果需要的话,让我们放大或放大 pdf。在这里,我们将其水平和垂直放大两倍:
在下面的代码块中,我们打开 SCANNED_FILE (pdf 扩展名),并将其转换为扩展名为 png 的图像。然后,我们利用 for 循环从文件中提取所有页面,脚本每页生成一个 png 图像。
现在我们有了一个图像文件,我们准备预处理这个图像。
在下一个单元格中,我们将加载上一步生成的第一个(可能只有一个)页面。
然后,我们将图像转换为灰度。
作者截图
下一步被称为 Otsu 的阈值技术。阈值处理是指我们制作二进制图像文件。它获取图像的像素并将其转换成黑色或白色。使用 Otsu 的阈值技术,我们不需要自己设置阈值参数。相反,大津是为我们做的。
作者截图
接下来是我们代码的核心。
首先,rectangular_kernel
决定在文本周围画多大的矩形。在这种情况下,我们有(66,66)。这个数字越高,我们得到的盒子就越少,因为它通过将不同的单词聚集在一个盒子里来将它们处理成一个单元。反之,如果有(11,11),我们得到的盒子越多,因为它更独立地处理不同的单词。
我们可以看到下面的盒子的遮罩,其内核大小为(66,66)。
作者截图
在下一张图中,我们将内核大小改为(11,11)。
作者截图
最重要的一行是text = pytesseract.image_to_string(cropped, lang='lat', config='--oem 3 --psm 1')
,宇宙魔方将图像转换成文本(或字符串)。config 参数允许您指定两件事:OCR 引擎模式和页面分段模式。
OCR 引擎模式或“oem”允许您指定是否使用神经网络。
- 仅传统
- 神经网络 LSTM 引擎而已。
- 传统+ LSTM 发动机。
- 默认,基于可用的内容。
页面分割模式或“psm”允许您指定 OCR 工具应该进行哪种页面分割。例如,我们使用“1”表示“带有 OSD(方向和脚本检测)的自动页面分段”,因为我们的文档是多列的。否则,我将使用“4”来表示“假设一列可变大小的文本”。如需使用哪种“psm”的示例,请阅读来自 Nanonets 的本教程:
https://nanonets.com/blog/ocr-with-tesseract/
说到底,这就是我们的结果:
作者截图
今天就到这里吧!
结论
今天,我们看到了宇宙魔方 OCR 的实际应用。一旦你掌握了图像预处理的诀窍,它会是一个很有价值的工具。然而,如果你不喜欢预处理图像,并且你足够幸运有数字化的 PDF 来处理,你可能会更好地使用 PyMuPDF。
但是数字化的 pdf 呢?!查看使用 Python 中的 PyMuPDF 阅读多列 PDF。
谢谢你过来看我的帖子。希望 PyMuPDF 能像帮助我一样帮助你!你可以在 Github 上这里找到我用的笔记本。
敬请期待!
如果你想了解更多关于我从懒鬼到数据科学家的旅程,请查看下面的文章:
如果你正在考虑改变方向,进入数据科学领域,现在就开始考虑重塑品牌:
参考
什么是 ROC-AUC 以及何时不使用它
原文:https://towardsdatascience.com/read-this-before-using-roc-auc-as-a-metric-c84c2d5af621
没有完美的度量标准。
原象
了解指标隐藏和提升了什么。
当一个衡量标准成为目标时,它就不再是一个好的衡量标准——古德哈特定律
当您改进一个度量标准时,您主要是改进该度量标准有利于什么。因为没有一个指标可以衡量一切,盲目地追求一个指标可能是危险的。我想探索这对 ROC-AUC 意味着什么。
接收器操作特征曲线或 ROC 曲线用于评估二元分类器。二元分类器输出数据点属于正类的概率。正是我们为这个数字设定了一个阈值,并给它贴上了一个标签。如果概率高于阈值,我们说预测的类是正的(1),否则是负的(0)。调整阈值通常取决于用例。对于不捕捉正面例子的成本大于将负面例子误分类为正面例子的情况,可以选择较低的阈值,反之亦然。
ROC 曲线有助于我们将阈值之间的权衡可视化。该曲线下的面积(ROC-AUC)是该模型如何很好地分离不同阈值的正例和负例的总结。
将 ROC-AUC 可视化
下面的示例将带您了解一个玩具示例的 ROC-AUC 计算。
原象
上图显示了 7 个点的虚拟数据集的 ROC 曲线,其中有 4 个阴性(蓝色)和 3 个阳性(红色)示例。它们按照模型预测概率的升序排列(0.1、0.1、0.2、0.2、0.3、0.6、0.9)。我们在四个任意选择的阈值 T1、T2、T3 和 T4 测量真阳性率(TPR) 和假阳性率(FPR) ,值分别为 0、0.15、0.25 和 0.95。
【TPR】=(+ve 个预测概率高于阈值的例子)/(总正例) 【FPR】=(-ve 个预测概率高于阈值的例子)/(总负例)
例如,考虑 T3 (=0.25)。T3 以上的正例数为 2(满分 3),T3 以上的反例数为 1(满分 4)。因此,TPR = 2/3,FPR = 1/4。最终,我们可以计算每个阈值的 TPR 和 FPR ,并将其绘制在二维平面上,如上所示。这条曲线下的面积是 ROC-AUC。阈值越多,曲线越平滑,指标越精确。
AUC 的限制
可实现的最大 AUC 是 1。这是什么意思?
原象
根据该曲线,对于任何非零 FPR,TPR 将总是 1。从概念上讲,如果您选择一个阈值,并且只有一个负数据点越过它,则每个正数据点都会越过该阈值。只有当存在一个完美区分类别的阈值时,这才是可能的。
在不平衡数据集的情况下会发生什么?
在不平衡数据集的情况下,ROC-AUC 往往更乐观。考虑两个排序的数据集(表示为密度),
原象
如图所示,数据集 1 (D1)有 50 多个 ve 示例和 100 个 ve 示例。假设正例与反例的重叠是同质的。也就是说,任何两个重叠部分都有相同比例的正面和负面例子。这同样适用于数据集 2 (D2)。显然,D2 比 D1 更不平衡。
对于 D1 ,at 阈值= 0.5 ,
—精度= 50/(50+50) = 0.5 。
—回忆= 50/50 = 1 。
— F-1 评分=(2精度召回)/(精度+召回)=(2 * 0.5 * 1)/(0.5+1)=2/3。
D2 在 0.75 阈值,F1 得分(= 2/3 )。
现在让我们做一些有趣的事情。让我们看看 AUC。
原象
对于 D1,当你从阈值 0 到 0.5 时,TPR 保持不变(=1)。类似地,对于阈值为 0 至 0.75 的 D2,TPR=1。
对于 D1,在 0.5 FPR 之后,TPR 和 FPR 线性下降,因为它们是均匀混合的。D2 也是如此,排在 FPR 之后 0.25。但是 D2 的 AUC 比 D1 大,尽管他们有相同的 F1 最高分。观察到对于大多数阈值,不平衡数据集中的 TPR 更高。
这可能具有欺骗性。那么还有什么选择呢?
精确召回曲线
精度-召回曲线类似于 ROC 曲线,除了 Y 轴是精度,X 轴是召回。通过在不同阈值绘制曲线来计算 AUC。考虑上面讨论的同一个例子的 PR 曲线—
对于 D1,任何阈值在 0.5 之后,对于 D2,任何阈值在 0.75 之后,精度=0.5。对于 D1,0.5 之前的任何阈值和对于 D2,0.75 之前的任何阈值都有 recall=1。如你所见,曲线基本相同。1/3 是 D1 的精度,1/5 是阈值=0 时 D2 的精度。我们使用的例子过于简单了。然而,PR-AUC 对不平衡不太敏感。
一般建议
衡量标准的表现很重要。模型评估几乎不会停留在单一的数字指标上。要了解模型学到了什么,请查看模型犯了哪些错误?模型对哪些例子不自信?指标的最大收益来自哪里?
我热爱教学!如果你需要帮助理解数据科学、机器/深度学习或 Python 中难以理解的概念,请随时在这里申请辅导课程。
使用 PyTorch 数据集更快地读取. h5 文件
原文:https://towardsdatascience.com/reading-h5-files-faster-with-pytorch-datasets-3ff86938cc
使用弱洗牌的方法
最近,我使用牛津 Pet 数据集(参考文献中的引用)进行了一个多任务深度学习项目。虽然我能够通过使用 Colab 的 GPU 来克服极其缓慢的训练速度,但在加载数据时,我很快就遇到了瓶颈。尽管使用了一个庞大的多任务模型,对于一个 32 的批量,训练速度大约是 0.5 秒,而数据加载过程大约是 12…
在互联网上深入搜索后,我发现这是许多人(参见 PT 论坛、 S/O #1 和 S/O #2 )面临的问题,也是尚未得到充分解决的问题。我想到了一个 PyTorch 解决方案,我认为它适用于大多数情况。这使得我在 Colab 上的加载时间从 12 秒减少到 0.4 秒左右。本文将首先解释瓶颈,然后介绍解决方案。随后是实证结果和对理论保证的评论。我假设您了解 PyTorch 数据集,但是对 Python 中的类有足够的工作知识就足够了。
瓶颈从哪里来?
PyTorch 中创建标准数据集的方式基于torch.utils.data.Dataset
类。这些定制数据集有一个__getitem__
方法,在调用该方法时,它会加载一组给定索引的数据。我写的数据集是这样的:
完整的数据加载器可以在 GitHub 资源库中找到,这里。在数据集初始化时调用 _load_h5_file_with_data 方法,将. h5 文件预加载为生成器对象,以防止每次调用 getitem 时被调用、保存和删除。
然后,使用torch.utils.data.Dataloader
类,我定义了一个trainloader
来批量处理我的数据用于训练目的。这就是问题所在。
每次迭代trainloader
时,数据集中的__getitem__
方法被调用batch_size
次,以生成一批数据。这对于. h5 数据集来说是有问题的,因为从它们中查询数据的时间复杂度与对它们的调用次数成线性比例。因此,如果查询单个索引的开销是 x ,那么在我们的例子中,我们期望每批的总时间是 32*x 。
这里,列车循环的每一对(输入,目标)将由列车装载器查询数据集 32 次(由于 shuffle=True )而创建。 getitem 方法被调用了 32 次,每次都使用不同的索引。然后, trainloader 后端聚集来自 getitem 方法的单个(输入,目标)返回,返回一个包含 32 项的(输入,目标)对用于训练循环。
使用弱洗牌提高速度
要认识到的关键点是,. h5 文件的瓶颈来自于寻找数据所在的索引。找到随后出现的索引几乎要花上 10 分钟的时间!
这意味着查询索引i
处的数据的时间复杂度与查询索引i:i+batch_size
处的数据的时间复杂度几乎相同。因此,如果我们进行批处理,然后对批处理进行混洗(我称之为‘弱混洗’),那么我们将通过batch_size
因子来加快数据加载过程。下图显示了这种方法与标准洗牌方式的不同之处。
这种方法的明显含义是,模型中引入了一些偏差,因为我们不是在点的水平上洗牌。虽然人们可能试图在批次级别洗牌后逐点洗牌以减少偏差,但这是徒劳的,因为损失函数是可交换的。如下图所示。
对于 m >
弱洗牌实现
数据集不需要更改。这是因为来自__getitem__
的index
参数可以是一个索引列表。因此,如果我们能够传递一个列表list(range(i, i+batch_size))
,那么我们就能够通过对. h5 文件的一次查询获得整批数据。
这意味着需要改变的是数据加载器。为此,必须使用sampler
参数创建一个自定义加载方案。如下所示:
几个要点:
RandomBatchSampler
是生成索引i:i+batch_size
的自定义采样器BatchSampler
类批量采样RandomBatchSampler
Dataloader
的batch_size
参数必须设置为None
。这是因为batch_size
和sampler
不能同时设置
理论保证
迄今为止,就我们在模型中诱导的偏差而言,我还没有找到弱改组方法的理论保证。我已经在交叉验证和数学交流上问过这个问题,如果我得到他们的回复,我会更新这篇文章。在此之前,我会感谢任何建议!
关键要点
- 本文描述的方法适用于无法将全部数据加载到内存中的情况
- 使用标准加载方案从. h5 文件中加载批处理很慢,因为时间复杂度与对文件进行的查询数量成比例
- 瓶颈来自于定位第一个索引,任何后续的索引(顺序排列,中间没有间隔!)可以加载,几乎不需要额外的费用
- 通过使用批量采样,我们可以批量加载数据,从而将加载时间减少一倍
batch_size
- 这意味着数据将被弱混洗。
batch_size
的选择仅限于batch _ size<T21【m】T7。因此,这种解决方案不适合瓶颈来自训练时间的情况。
多任务项目的完整代码可以在这里找到。在不久的将来,我会发布更多关于这个项目的文章,敬请关注!
参考
[1] 推荐加载较大 h5 文件的方式。PyTorch 论坛。
[2] torch.utils.data 。PyTorch 文档。
[3] 如何在 Dataloader 中使用 Batchsampler。销售订单
[4] 是否有更有效的方法从 hdf5 数据集中检索批次?销售订单
[5] 读取. h5 文件极其缓慢。销售订单
[6] 从数据加载器随机批量取样。PyTorch 论坛。
[7] 在 PyTorch 中创建一个定制的数据加载器。中等。
[8] 使用 h5py 混洗 HDF5 数据集。销售订单
[9]py torch 的 HDF5 数据集。中等,
数据集
[1] 牛津宠物数据集。许可:知识共享署名-共享 4.0 国际许可。
所有图片均来自作者,除非另有说明
用 Python 实现实时异常检测
原文:https://towardsdatascience.com/real-time-anomaly-detection-with-python-36e3455e84e2
使用 PyOD 和 PySAD 进行流数据的机器学习
用于实时异常检测的滑动窗口(图片由作者提供)
在这篇博文中,我们将讨论流数据的异常检测,特别是 Python 的两个库 PyOD 和 PySAD。
简而言之,异常检测是对不符合预期模式的项目、事件或观察结果的识别。这可能像检测欺诈性信用卡交易一样简单,也可能像识别制造数据中的违规行为一样复杂。
异常检测可以帮助您在问题变成问题之前识别问题,并通过识别您可能没有意识到的模式来帮助您优化流程。
说到大数据,主要有两类:批处理和流处理。批处理是指一次性处理所有数据。另一方面,流式传输是指在数据到达时对其进行处理。由于流数据不断更新,异常检测是保持系统平稳运行的关键。
在这篇博文中,我们将通过在几行代码中结合 PyOD 和 PySAD 来解决实时检测异常的挑战。
敬请期待!
异常检测
异常检测是对不符合预期模式或数据集中其他项目的观察值的识别。这是一个重要的数据挖掘工具,用于发现诱发的错误、意外的模式和更普遍的异常。
执行异常检测有多种方法,您采用的方法取决于数据的性质和您要寻找的异常类型。但是一般来说,对于给定的样本分布,有三种方法可以识别可能被认为是异常的观察值:
无监督(来源:PyOD documentation)当训练数据同时包含正常和异常观测值时,该模型在拟合过程中识别出异常值。当“异常值”被定义为存在于数据集的低密度区域中的点时,可以使用该方法-任何不属于高密度区域的新观察值都将由算法自动标记。
半监督(来源:PyOD documentation)
半监督技术背后的想法是,我们可以利用我们关于什么使事情正常的知识,并在无异常的数据点上拟合模型。然后,在推断时间期间检测异常,并假设异常随训练数据的分布而变化。
监督(资料来源:PyOD documentation)
每个观察的基本事实——内部和外部——都是众所周知的。该模型已经对不平衡的训练数据进行了拟合,然后用于对新的观察结果进行分类。当属于异常值类的内容与属于其他类的内容之间存在明显差异时,以及知道它们随后的行为时,可以采用这种方法-假设异常值具有与训练集中相似的分布。
实时异常检测
为了检测异常,机器学习算法必须能够识别数据中的模式。
这些模式可能非常复杂,并且会随着时间的推移而变化。
这就是为什么我们有时需要在生成新数据点的同时在线更新机器学习模型的原因。
出于多种原因,实时异常检测非常重要。首先,它可以帮助您快速识别并纠正错误,以免造成重大损失或中断。其次,它可以让您主动监控系统的潜在问题,并在检测到问题时快速做出响应。最后,它可以让您近乎实时地了解系统中正在发生的情况,让您做出更好的决策,也就是说,在异常发生时而不是事后检测到异常。
机器学习算法的问题在于,它们的训练速度相当慢,而且通常需要大量数据。根据我们试图达到的推理时间,我们应该小心我们选择的模型。正如您所想象的,在速度和准确性之间进行权衡,在使用 PySAD 时有两个主要参数来处理这种折衷:“窗口大小”和“滑动大小”。
窗口大小决定了机器学习算法用来训练模型的数据点的数量,滑动大小是模型更新的周期。
例如,如果我们想检测 Twitter feeds 中的异常,我们就在一个历史窗口上训练算法,并用这个模型检查每个新推文,以确定它是否是异常。然后,在“滑动大小”时间(比如 30 秒)后,模型太旧了,我们在一个新窗口上更新模型,该窗口查看最近的数据点。
这意味着模型定期更新,但不是每次有新的数据点时都更新。
实时异常检测框图(图片由作者提供)
管道与 PyOD 和 PySAD
我最喜欢的两个异常检测库是 PyOD 和 PySAD。PyOD 库是一个全面的 Python 工具包,用于检测多元数据中的异常值,而 PySAD 是一个轻量级库,用于流数据中的无监督异常检测。
这两个库都是开源的,易于安装,并且彼此兼容。
我们可以将这两个库结合起来,与前面描述的窗口模型一起工作:PySAD 负责窗口逻辑,而 PyOD 拥有检测异常的算法。
用于实时异常检测的管道(图片由作者提供)
在实时处理数据时,预处理可以发挥至关重要的作用,因为如果您使用一些降维算法(如主成分分析(PCA )),可以显著提高算法的速度,或者可以通过归一化传入的数据点来提高算法的准确性。
另一方面,后处理是从异常分数中去除噪声所必需的。在管道的末端,概率校准器会给你面对异常的最终可能性。
整个流水线——预处理、窗口模型、后处理和概率校准器——可以用几行代码实现。
这里有一个笔记本展示了合并这两个库的简单性:
结论
异常检测是识别不符合预期模式的事件或观察结果的过程。由于数据生成的高速率和必须处理的数据量,检测流数据中的异常可能很困难。
如果您应该从本文中记住一件事,那就是 PyOD 和 PySAD 可以一起使用来检测数据流中的异常,并且这样做的代码只有几行长。
为了检测异常,机器学习算法必须能够识别数据中的模式,但这些模式可能非常复杂,可能会随时间而变化。由于这个原因,当新的数据点被创建时,我们有时需要在线更新机器学习模型。
好奇想了解更多关于 Anthony 的工作和项目吗?在中、 LinkedIn 、 Twitter 上关注他。
需要技术写手?将您的请求发送到https://amigo CCI . io。
使用 Kafka、BigQuery 和 Looker Studio 构建实时事件流管道
原文:https://towardsdatascience.com/real-time-event-streaming-with-kafka-bigquery-69c3baebb51e
一个简单的数据工程项目
实时流媒体应用有时可能会很复杂,当试图了解它们时,决定一个实际的用例对于培养有趣有效的学习体验是很重要的。因此,通过下面的例子,我希望您能够以一种简单的方式掌握构建实时应用程序的基础。
方案
假设我们在一家音乐流媒体服务公司的数据工程部门工作,我们需要创建一个实时仪表板,显示某个特定艺术家(比如说 【托尼·阿伦】 )一段时间内最受欢迎的歌曲。为此,我们将利用一个流行的分布式流媒体平台 Kafka 来制作、消费必要的歌曲事件,并将其流式传输到 BigQuery 中,这样我们就可以在 Looker Studio 的仪表盘上可视化这些流行歌曲。
我们的架构最终会是这样的:
实时流架构—作者图片
术语和概念
让我们快速定义一些我们将在本文中涉及的术语。
- Kafka:Apache Kafka是一个开源的分布式流媒体平台,支持(除其他外)实时事件驱动应用程序的开发,非常适合我们的用例。
- **Kafka 集群:**一组服务器(称为代理),它们协同工作,为实时应用程序提供高可用性、容错和存储。
- 如上所述,代理是在 Kafka 集群中执行实际工作的机器。它托管一些分区集,处理向这些分区写入新事件的传入请求,并允许消费者按主题、分区和偏移量获取消息。
- 主题:一个主题仅仅是一个 事件日志 。来自生成器的每个新事件都被附加到主题的末尾。而话题又分为分区。
作者图片
- Producer: 您编写的将数据发布(产生)到 Kafka 集群中的主题的应用程序。
- **消费者:**从 Kafka 集群中实时检索数据的应用程序或最终用户。为了有效地获取实时消息,Kafka 消费者必须订阅集群中存在的各个主题。
- Zookeeper :跟踪 Kafka 集群节点的状态,它还跟踪 Kafka 主题、分区等等。(注:一个名为 的更新 KIP-500 移除了对 Zookeeper 的需求,但我们在本文中将不会使用那个版本的 Kafka)。
- Poll:
poll()
方法是 Kafka 消费者调用的从给定主题中检索记录的函数。
我们将在 4 个步骤中设置上述架构。但是在我们开始之前,请确保您具备以下先决条件:
先决条件
- 确保你已经安装了Docker。
- 在您的机器上安装
[confluent-kafka](https://pypi.org/project/confluent-kafka/)
Python 库。 - 启用 BigQuery API 。
- 在 Google Cloud 中创建一个服务帐户密钥,并提供流 API 工作所需的权限。将它保存在您的机器上,因为我们稍后会引用它。
#1.用 Docker 部署 Kafka
Kafka 可以通过多种方式部署,但是我们将使用 Docker 来部署它,因为它非常简单。
我们的 Kafka 集群将有两个主要实体;
- 1 个代理实例和
- 1 个 Zookeeper 实例。
简单的卡夫卡集群——作者图片
我们将使用一个 Docker 组合文件来配置和运行这些容器。您会注意到下面的docker-compose.yaml
文件中公开的 2 个服务和所需的端口:
确保 docker 文件与我们稍后将要编写的 Kafka 生产者和消费者文件位于同一个目录中。
要构建两个 Docker 容器,运行这个命令,您应该在几分钟内就可以启动并运行这两个容器。
docker-compose up -d
#2.构建生成器
我们将编写一个模拟音乐流平台上用户活动的应用程序/制作程序。这个应用程序将发送一个名为song-completed
的事件,当用户完成一首歌曲时就会触发这个事件。这个事件将被发送到一个我们称之为tony-allen-plays
的卡夫卡主题。
一个生产者和消费者在 Kafka 集群中与主题互动的架构——作者图片
我们将使用 Faker 包为我们的应用程序生成假的流数据。我们的假事件有效负载看起来像这样:
{'user_id':001,
'artist': 'tony-allen',
'song_id': 03,
'song_name': 'lady',
'event_type':'song_completed',
'timestamp': '2022-11-03 07:22:13'}
要安装 Faker 包,请在终端窗口中运行:
pip install Faker
生成一个假歌曲列表
现在,在我们的代码中,我们将启动 Faker 对象,并创建一个硬编码的托尼·阿伦 10 首随机歌曲的歌曲列表,它将成为事件有效负载的一部分。
我从他在谷歌上的歌曲列表中随机挑选了一些歌曲——作者截图
from confluent_kafka import Producer
from faker import Faker
import json
import time
import logging
#Create Faker object to generate fake data for Producer
fake=Faker()
#Create Tony Allen song list
songs = ["zombie", "lady", "secret-agent","kindness","soldiers","asiko","the-same-blood","upside-down","african-man","vip"]
配置日志格式
每当一个新事件可用时,日志将被附加到主目录中的一个producer.log
文件中——我们在下面定义了它。这里,我们正在设置我们希望如何格式化该日志文件的基本配置。
#Configure logger
logging.basicConfig(format='%(asctime)s %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
filename='producer.log',
filemode='w')
logger = logging.getLogger()
logger.setLevel(logging.INFO)
发起生产者
通过指定 Kafka 集群的端口来启动 Kafka producer 对象,如上面的 Docker compose 文件中所定义的:
#Create Kafka Producer
p=Producer({'bootstrap.servers':'localhost:9092'})
配置回拨
定义一个负责确认新消息或错误的回调函数。当有效消息可用时,它被解码为 utf-8 并以首选格式打印。相同的消息也会附加到日志文件中。
#Callback function
def receipt(err,msg):
if err is not None:
print('Failed to deliver message: {}'.format(err))
else:
message = 'Produced message on topic {} with value of {}\n'.format(msg.topic(), msg.value().decode('utf-8'))
logger.info(message)
print(message)
编写一个生产者循环
这是有趣的部分!这里我们只是创建了一个 3 秒延迟的循环,模拟流媒体平台上的实际用户活动。我们为 JSON 事件创建了一个模式,并利用 Faker 来生成实际的数据点。
#Write Producer loop
def main():
for i in range(20):
random_song_id = fake.random_int(min=0, max=9)
data={
'user_id': fake.random_int(min=20000, max=100000),
'artist': 'tony-allen',
'song_id': random_song_id,
'song_name': songs[random_song_id],
'event_type':'song_completed',
'timestamp': str(fake.date_time_this_month())
}
m=json.dumps(data)
p.produce('tony-allen-plays', m.encode('utf-8'),callback=receipt)
p.poll(1) # Polls/checks the producer for events and calls the corresponding callback functions.
p.flush() #Wait for all messages in the Producer queue to be delivered. Should be called prior to shutting down the producer to ensure all outstanding/queued/in-flight messages are delivered.
time.sleep(3)
注意,当我们调用p.produce
时,我们指定了我们想要发布消息的 Kafka 主题。在这种情况下,称为tony-allen-plays
。因为这个主题在我们的 Kafka 集群中还不存在,所以它是在这个应用程序第一次运行时自动创建的。
p.poll
很重要,因为它检查事件的生产者并调用我们之前定义的相应回调函数。
我们完整的producer.py
脚本应该是这样的:
要确认生成器按预期工作,请在终端窗口中运行以下命令:
python producer.py
您应该看到下面的输出,它打印出每 3 秒钟发送到 Kafka 主题的事件。
终端窗口中的生产者输出—按作者分类的图像
#3.建立消费者
消费者会做两件主要的事情:
- 从
tony-allen-plays
主题中轮询和检索事件 - 使用 BigQuery 流 API 将这些事件作为流发送到 BigQuery
安装 BigQuery Python 库
首先,使用以下命令安装 BigQuery Python 库。
pip install google-cloud-bigquery
然后我们可以将它导入到consumper.py
脚本中,并设置 BigQuery 配置。
from confluent_kafka import Consumer
from google.cloud import bigquery
import ast
from google.oauth2 import service_account
#Create BQ credentials object
credentials = service_account.Credentials.from_service_account_file('PATH-TO-BQ-SERVICE-ACCOUNT')
# Construct a BigQuery client object.
bq_client = bigquery.Client(credentials=credentials)
#Speficy BigQuery table to stream to
table_id = 'PROJECT-ID.DATASET.TABLE-NAME'
启动消费者
接下来,我们通过指定端口来启动 Kafka 消费者,然后我们订阅主题tony-allen-plays
。在初始化消费者时,我们指定消费者 groupid,因为所有 Kafka 消费者必须属于一个消费者组。
c=Consumer({'bootstrap.servers':'localhost:9092','group.id':'tony-allen-consumer','auto.offset.reset':'earliest'})
print('Kafka Consumer has been initiated...')
#Subscribe to topic
c.subscribe(['tony-allen-plays'])
您还会注意到有一个属性——auto.offset.reset
——最早被设置为‘T5’。基本上是从话题划分开始就在告诉消费者消费。
卡夫卡 auto.offset.reset:最早的 —作者图片
典型的 Kafka 消费应用程序以消费循环为中心。因此,最后一步是编写一个循环,不断地轮询主题中的新消息,如果发现新消息,就将这些消息发送给 BigQuery。
我们完整的脚本应该是这样的:
卡夫卡消费者. py
运行 Kafka 管道
既然已经设置了消费者和生产者,那么打开两个单独的终端窗口并再次运行生产者:
python producer.py
然后运行消费者,以便它实时从主题中读取数据:
python consumer.py
如果您看到生产者生成的消息开始出现在消费者终端窗口中,那么您的消费者正在正常工作,数据也应该流入 BigQuery:
Kafka consumer.py 输出—作者图片
《大查询》中的卡夫卡事件——作者图片
#4.可视化数据
最后一步是将 BigQuery 表连接到 Looker Studio,并创建一个简单的条形图,以接近实时的方式显示流行歌曲。
前往 Looker Studio ,签到并:
- 选择新的“空白报告”
- 在“连接到数据”下,选择“BigQuery”作为数据源
- 然后选择您的 BigQuery 项目、数据集和表
现在您应该看到一个类似的视图。确保尺寸和度量字段与下面的屏幕截图匹配,您应该有一个如图所示的简单条形图。
Looker 工作室截图作者。“总播放次数”由“记录计数”更名而来。—作者图片
接近实时的仪表板
Looker Studio 有一个 数据刷新 特性,它指定了仪表板刷新的频率。您可以将其设置为 1 分钟,这是当前可用的最频繁的刷新周期,您的控制面板应该每 1 分钟刷新一次。
结论
我们讲述了如何用 Docker 建立一个最小的 Kafka 集群,将数据加载到一个主题中,然后消费数据并将其传输到 BigQuery。最后,我们创建了一个近乎实时的仪表板,在 Looker Studio 中呈现最终结果。
我希望你觉得这很有用,并祝你好运建立你的下一个实时应用程序!欢迎分享您的任何建议或其他使用案例。
你可以成为中会员支持我,享受更多这样的故事。
参考
使用 Elasticsearch 进行实时预输入搜索(AWS OpenSearch)
使用 MovieLens 数据集在云上构建可扩展的智能搜索引擎的端到端示例
在谷歌搜索的例子。作者图片
1。
简介 2。数据集准备
3。设置 OpenSearch
4。索引数据
5。基本查询同匹配
6。用 Jupyter 笔记本和 ipywidgets
7 基本前端实现。一些高级查询
∘ 7.1 匹配短语前缀
∘ 7.2 匹配+前缀带布尔
∘ 7.3 多字段搜索
8。结论
关于我的
参考文献
1.介绍
你有没有想过谷歌是如何让它的搜索引擎变得如此智能,以至于它可以预测我们的想法,甚至在我们不输入整个内容的情况下自动完成整个搜索词?它被称为类型预搜索。这是一个非常有用的语言预测工具,许多搜索界面使用它来为用户输入查询提供建议。[1]
作为一名数据科学家或任何从事数据后端工作的人,有时我们可能需要这样一个交互式搜索引擎界面,让我们的用户能够以最少的努力查询结构化/非结构化数据。这样总能让用户体验更上一层楼。
幸运的是,我们不必从头开始构建它。有很多开源工具可以使用,其中之一就是 Elasticsearch 。
elastic search是一个分布式、免费和开放的搜索和分析引擎,适用于所有类型的数据,包括文本、数字、地理空间、结构化和非结构化数据。Elasticsearch 以其简单的 REST APIs、分布式特性、速度和可伸缩性而闻名,是 Elastic Stack 的核心组件,是一套用于数据摄取、丰富、存储、分析和可视化的免费开放工具。[2]
另一方面,亚马逊创建的 AWS OpenSearch 是 Elasticsearch 的分叉版本,适合其 AWS 生态系统。它有一个非常相似的界面与基础结构与弹性搜索。在这篇文章中,为了简化在本地机器上下载、安装和设置 Ealsticsearch 的过程,我将带您浏览一个使用 AWS Open Search 索引和查询数据的端到端示例。
在现实世界中,使用这种云服务的另一个重要原因是**可扩展性。**我们可以轻松调整所需的资源,以适应任何数据复杂性。
请记住,即使我们在这里使用 AWS OpenSearch,如果您已经设置了 Elasticsearch,您仍然可以按照其中的步骤操作。这些工具在本质上非常相似。
2。数据集准备
在本例中,我们将使用 MovieLens 20M 数据集 ,这是一个受欢迎的开放电影数据集,被许多数据专业人员用于各种项目。之所以称之为 20M,是因为数据集中包含了 2000 万个收视率。此外,整个数据集中包含 465,000 个标签应用、27,000 部电影和 138,000 个用户。
这个数据集包含几个文件,可以用于非常复杂的例子,但是假设我们只想在这里构建一个可以查询电影名称、年份和流派的电影搜索引擎,我们只需要一个文件 movies.csv 。
这是一个非常干净的数据集。结构如下所示:
movies.csv (MovieLense 20M)。作者图片
只有 3 个字段: movieId 、片名(括号内为年份)、以及流派(用|)隔开。我们将使用标题和流派对数据集进行索引,但看起来有些电影没有指定流派(例如,movieId = 131260),因此我们可能希望将这些流派替换为 NA,以防止它们作为不需要的流派关键字被查询。几行处理就足够了:
import pandas as pd
import numpy as npdf = pd.read_csv('../data/movies.csv')
df['genres'] = df['genres'].replace('(no genres listed)', np.NaN)
df.to_csv('../data/movies_clean.csv', index=False)
通过这一小段代码,我们刚刚清理了数据集,并将其保存为一个名为movie_clean.csv
的新文件。现在,我们可以继续创建一个 AWS OpenSearch 域。
3.设置开放搜索
下面是来自 AWS OpenSearch 的 官方文档 。你可以跟着它做更详细的介绍,也可以通读我下面做的简化版。
如果你没有 AWS 账号,可以按照这个 链接 报名 AWS。您还需要为 AWS 服务添加一种支付方式。然而,不要惊慌,因为在本教程中,我们将使用最少的资源,成本应该不超过 1 美元。
报名 AW S .图片作者
创建您的帐户后,只需登录您的 AWS 管理控制台 ,搜索 OpenSearch 服务,或点击此处进入 OpenSearch 仪表盘。
打开搜索仪表板。作者图片
在仪表板中,按照以下步骤操作:
- 选择创建域。
- 给个域名。
- 在开发类型中,选择开发和测试。
AWS OpenSearch 设置。用户图像
4.将实例类型更改为 t3.small.search ,并保留所有其他类型的默认值。
AWS OpenSearch 设置。用户图像
5.为了项目的简单,在网络中,选择公共接入
6.在细粒度访问控制中,通过设置用户名和密码来创建主用户。
AWS 开放搜索设置。用户图像
7.在访问策略中,选择只使用细粒度的访问控制
AWS OpenSearch 设置。用户图像
8.通过保留默认设置来忽略所有其他设置。点击创建。这可能需要 15-30 分钟才能完成,但根据我的经验,通常会更快。
4。索引数据
AWS OpenSearch 或 Elasticsearch 足够智能,可以自动索引我们上传的任何数据,之后我们可以用任何逻辑规则编写查询来查询结果。然而,可能需要一些预处理工作来简化我们的查询工作。
我们记得,我们的数据由 3 列组成:
movies.csv (MovieLense 20M)。作者图片
标题和类型对我们来说都很重要,因为我们可能希望在任一/两者中输入任何关键字来搜索我们想要的电影。open search 支持多字段搜索,但是为了查询的简单性,我们也可以通过将我们感兴趣的所有关键词放入一个专门的列来对其进行预处理,这样提高了效率,降低了查询复杂度。
预处理以创建新的 search_index 列。作者代码
使用上面的预处理代码,我们将一个名为 search_index 的新列插入到包含标题和所有流派的数据帧中:
添加了 search_index 的数据帧。作者图片
下一步是将数据转换成 JSON 格式,以便批量上传到我们的域。为批量数据上传指定的格式可以在 开发人员指南 选项 2 中找到。大概是这样的:
{"index": {"_index": "movies", "_id": "2"}}
{"title": "Jumanji (1995)", "genres": "Adventure|Children|Fantasy", "search_index": "Jumanji (1995) Adventure Children Fantasy"}
其中第一行指定要保存在域中的索引(文档)名称以及记录 id(这里我使用 movieId 列作为惟一标识符)。第二行包括数据集中的所有其他字段。
以下代码用于转换:
从 dataframe 转换到 JSON。作者代码
转换后,数据以movies.json
的名称存储在数据文件夹中。现在我们需要将它上传到域中,如下所示:
将 JSON 数据批量上传到域中。作者代码
请注意,端点可以在您的 OpenSearch 域页面上找到。用户名和密码是我们在创建该域名时设置的主用户名&密码。
域仪表板。作者图片
如果它返回一个 <响应【200】>,那么我们就可以开始了。数据集已成功上传到 AWS OpenSearch 域。
5.带有匹配的基本查询
现在,随着数据的上传,我们已经完成了服务器端的所有工作。OpenSearch 自动索引数据以备查询。我们现在可以开始在客户端上查询来自域的数据。
要了解关于查询语言的更多信息,这里有两个选项:
- 开始使用 AWS OpenSearch 服务开发者指南
- 在这个 官方 Elasticsearch 指南查询 DSL 中有一些关于从 Elasticsearch 和 OpenSearch 中查询数据的非常详细的文档。
然而,在这个例子中,我们不需要非常高级的功能。我们将主要使用围绕标准 匹配查询 的一些小变化。
下面是一个基本的例子:
基本匹配查询。作者代码
在这里,我们编写查询来查找任何与 title = "jumanji "匹配的记录,将 JSON 查询序列化为字符串,并将其与端点和凭证一起发送到域。让我们看看返回的结果:
{'took': 4,
'timed_out': False,
'_shards': {'total': 5, 'successful': 5, 'skipped': 0, 'failed': 0},
'hits': {'total': {'value': 1, 'relation': 'eq'},
'max_score': 10.658253,
'hits': [{'_index': 'movies',
'_type': '_doc',
'_id': '2',
'_score': 10.658253,
'_source': {'title': 'Jumanji (1995)',
'genres': 'Adventure|Children|Fantasy',
'search_index': 'Jumanji (1995) Adventure Children Fantasy'}}]}}
正如我们看到的,它返回标题等于jumanji
的记录。我们的数据集中只有一个匹配的结果,确切的标题是Jumanji (1995)
,还有其他信息,如 id、流派和 search_index。
OpenSearch 自动处理大写/小写字母、任何符号和空格,因此它可以很好地找到我们的记录。此外,分数意味着返回的结果与我们的查询匹配的可信度,越高越好。这种情况下是10.658253
。如果我们在搜索查询中包括年份,比如“jumanji 1995”,那么分数将增加到16.227726
。当查询返回多个结果时,对结果进行排序是一个重要的指标。
6.Jupyter 笔记本和 ipywidgets 的基本前端实现
作为一个数据科学家, Jupyter 笔记本是一个很好的朋友,配合现在流行的 ipywidgets ,我们可以让笔记本变得很有互动性。下面是构建一个基本 GUI 的一些代码,该 GUI 包括一个文本框(用于输入关键字)和一个文本输出(用于显示查询结果)。
该准则有 5 个部分:
- 搜索功能:上一节介绍的基本查询的包装器。通过输入,它搜索并返回包含输入关键字的结果。
- 加粗:这个功能使用 Markdown 来加粗结果中的关键词,以便更好地可视化结果。
- printmd :在 IPython 中显示 Markdown 的包装函数
- text_change :处理 widget 事件的函数。在这种情况下,只要文本框中的值发生变化,它就会执行搜索功能,并返回按分数排序的前 10 个结果。还实现了一个计时模块来显示搜索需要多长时间。
- 代码的最后一部分定义了要显示的小部件元素:一个文本框和一个输出。当框中的值改变时,小部件事件被触发,结果将显示在输出中。
下面是我们执行代码时的样子:
使用 GUI 的基本查询。作者图片
当我们输入关键字“jumanji”时,它会不断搜索更新的关键字。在此过程中,关键字“j”、“ju”和“jumanji”分别返回 3 组结果。但是,对于“jum”、“juma”、“juman”或“jumanj”,它是空的,因为我们在这里使用的匹配查询将输入的关键字视为精确术语,并且我们的数据集中没有包含这些单词的电影。
这看起来不像我们预期的 typeahead 功能。为了提高性能,我们将在下面研究一些高级查询选项,包括前缀、布尔和多字段搜索。
7.一些高级查询
7.1 匹配短语前缀
一个简单的解决方法是使用 匹配短语前缀查询 代替 匹配查询 。匹配短语前缀查询可以按照与所提供的相同的顺序返回包含所提供文本的单词的文档。所提供文本的最后一个术语被视为前缀**,匹配以该术语开头的任何单词。[5]******
这似乎是我们想要的:当我们输入查询时,我们不必在它可以告诉结果之前完成术语。
所以让我们这样修改我们的查询!
这里我不会再展示 Python 代码,因为除了对搜索函数中的查询稍作修改之外,一切都保持不变。现在match
变成了match_phrase_prefix
:
query = {
'query': {
'match_phrase_prefix': {
'title': prefix
}
}
}
更改后,现在看起来是这样的:
匹配短语前缀查询的结果。作者图片
我们在这里尝试了 3 个例子:
- 巨漫记:现在修好了!当我们输入时,搜索引擎使用输入的文本作为前缀,完美地呈现所有想要的输出。
- 《哈利·波特》:和上一个例子做的一样。当我们到第二个学期“波特”时,它开始显示所有的哈利波特电影。
- 哈利波特:然而,如果我们将“哈利波特”反转为“哈利波特”,它不会显示任何结果,因为在匹配短语前缀查询中顺序很重要。
想象一下,当我们使用搜索引擎时,我们并不总是 100%确定我们记住了按正确顺序排列的所有关键词,,所以我们可能想要改进搜索引擎,使它变得更加智能来帮助我们解决问题。
7.2 用布尔值匹配+前缀
要分解此类查询的需求**😗*
- ****最后一项需要作为前缀处理
- 其他术语需要与完全匹配
- 我们不需要确切的顺序来得到想要的结果
遗憾的是,Elasticsearch/OpenSearch 中没有这种我们可以直接使用的查询类型,但是工具足够灵活,我们可以使用 布尔查询 来实现逻辑。
search_prefix 函数,它将最后一项视为前缀,而忽略顺序。作者代码
上面显示的代码是更新后的搜索功能。正如我们在这里看到的,在合并了 bool 操作 must 之后,查询变得更长了:
- 如果我们输入的搜索文本包含不止一个单词,那么我们将把最后一个单词视为前缀,和,前面的单词与完全匹配。****
- 如果搜索文本是只有一个单词**(或更少),那么我们就把它当作一个前缀来搜索。**
实施更改后,让我们看看结果:
使用前缀和布尔语句的复合查询的结果。作者图片
现在,当以完全混合的顺序输入:“波特哈利王子血”时,搜索引擎知道我们在寻找什么,它能够返回正确的结果:哈利波特与混血王子(2009) 。
7.3 多字段搜索
到目前为止,我们只在电影标题字段中进行搜索,但是我们可以进一步改进搜索引擎,通过利用一些隐藏字段,例如我们案例中的类型**,使其更加智能。**
要在多个字段中进行搜索,弹性搜索用户指南中有 多匹配查询 。但是,它没有提供足够的能力来搜索前缀,同时像我们刚才所做的那样忽略订单。我们仍然需要布尔来进行复合查询。
有一个捷径:记住,我们之前处理了数据集,并准备了一个组合的 search_index 列来包含标题和流派信息。有了这个新字段,我们可以很容易地修改我们以前的查询,以包含隐藏的流派信息,而无需显式地使用多匹配查询。
search_prefix 函数,在不显式使用多匹配的情况下进行多字段搜索。作者代码
这是修改后的search_prefix
函数,但是如果你仔细看,它和上一个函数没有什么变化,除了我们用search_index
代替了title
来搜索标题和流派。
以下是更改后的结果:
使用标题和类型信息的多字段搜索结果。作者图片
我们尝试了两个例子:
- 假设我们记得有一部冒险电影,关键词是“不可能”。然后我们输入“不可能的冒险”,它会返回所有不可能完成的任务的电影,因为它们被归类为“冒险”电影。
- 哈利·波特也一样。如果输入“波特冒险”,它将返回所有的哈利波特电影,因为它们都是“冒险”电影。
8.结论
在这篇文章中,我们通过一个端到端的例子来构建一个智能的预输入搜索引擎,步骤如下:
- 准备电影数据集
- 设置 AWS OpenSearch 域
- 将数据集批量上传到 OpenSearch 服务进行索引
- 构建一个简单的预输入搜索引擎 GUI
- 从基础到高级主题,从域中查询数据的几种方法
结果令人印象深刻:我们能够像谷歌一样模仿预输入搜索。在键入关键字的同时,搜索引擎在中近乎实时** (0.2 秒)地分析和搜索数据库,以呈现最接近的匹配,并指导我们进一步缩小搜索结果列表的方向。**
另一方面,我们使用布尔运算修改了查询,使我们能够混合关键字顺序**。我们还可以输入属于隐藏字段的值,例如流派名称,以帮助我们缩小范围并找到想要的结果。这两个特性使得搜索引擎更加智能。**
在这个例子中,我们还有很多地方可以在将来改进:
- Jupyter 笔记本和它的
ipywidgets
在构建 GUI 方面非常强大。这里我们只使用了文本框和输出,但是本例中可以使用更多的小部件,例如触发搜索的按钮**、在结果上添加过滤器的复选框或下拉列表,或者点击 Google 提供的建议结果的选择框。** - 然而,与前端开发人员使用的工具包(如 JavaScript)相比,
ipywidgets
仍然具有有限的功能**。本例中显示的工作流仅用于数据科学家的快速测试和开发用途,他们非常熟悉 Jupyter 笔记本电脑,但在前端开发方面经验有限。如果我们想建立一个强大的搜索引擎应用程序,这不是最好的做法。** - Elasticsearch/OpenSearch 领域特定语言(DSL) 是一个非常通用的工具,我们可以用它来编写几乎可以满足任何需求的查询。在这个例子中,我们只是覆盖了它的一个非常肤浅的层面。如果你有兴趣了解更多,这里有 Elasticsearch 提供的完整的 文档 。
- AWS OpenSearch 是可扩展的**。在这个例子中,我们为这个 27,000 部电影的数据集选择了
t3.small.search
,但是如果我们扩大资源,我们通常可以期待更好的结果。在现实世界中,数据量也可能是完全不同的数量级,因此需要为实际的数据大小选择适当的资源。** - 对于任何云服务来说,安全性都是一个严肃的话题。在这个例子中,我们几乎完全忽略了所有的安全设置,但是如果是在生产环境中,我们需要付出更多的努力来增强安全级别。
在帖子的最后,最后提醒一句:如果你不再需要 AWS OpenSearch 域名,请记得将其删除,否则会产生不必要的费用!****
感谢您的阅读!如果你喜欢这篇文章,请关注我的频道和/或 成为我今天的推荐会员 (非常感谢🙏).我会继续写下去,分享我关于数据科学的想法和项目。如果你有任何问题,请随时联系我。
**https://zhouxu-ds.medium.com/membership
关于我
我是赛诺菲的数据科学家。我拥抱技术,每天都在学习新技能。欢迎您通过媒体博客、 LinkedIn 或 GitHub 联系我。我的观点是我自己的,而不是我雇主的观点。
请看我的其他文章:
- 利用空气质量传感器数据进行时间序列模式识别
- 利润最大化的贷款违约预测
- 使用 Berka 数据集进行贷款违约预测
- 使用 Python 和 Flask-RESTful 为机器学习模型构建 REST API
- 理解分类问题中的 Sigmoid、Logistic、Softmax 函数和交叉熵损失(对数损失)
参考文献
[1]动态 Web-type ahead 搜索:https://doc . Dynamic Web . com/documentation-9/how-tos/general/implementing-type ahead-Search
[2]弹性——什么是弹性搜索:https://www.elastic.co/what-is/elasticsearch
[3] MovieLens 20M 数据集:https://grouplens.org/datasets/movielens/20m/
F.麦克斯韦·哈珀和约瑟夫·a·康斯坦。2015.电影镜头数据集:历史和背景。ACM 交互式智能系统汇刊(TiiS) 5,4,第 19 篇(2015 年 12 月),19 页。http://dx.doi.org/10.1145/2827872
[4]亚马逊 OpenSearch 服务开发者指南:https://docs . AWS . Amazon . com/open search-Service/latest/Developer Guide/gsg . html
[5] Elasticsearch Guide —查询 DSL:https://www . elastic . co/Guide/en/elastic search/reference/current/Query-DSL . html**
网站访客预测与脸书先知:完整教程
包括安装说明、数据、参数调整以及简单和复杂的预测—第 1 部分
预测可能会拯救你或你的公司。美国国家海洋和大气管理局在 Unsplash 拍摄的照片
作为微软的一名数据分析师,我每天都必须调查和理解时间序列数据。除了在历史背景下查看一些关键绩效指标,如 SRPVs、CPCs、转换率,我们有时还需要展望未来,即进行预测。对当前数据和未来的洞察有助于我们和我们的客户提前调整。
我试过很多方法。我个人最喜欢的是 LSTM。但是在现在的公司,他们雇佣了 FB Prophet。入乡随俗。因此,在这个项目中,我将坚持 Prophet 来预测欧洲某网站的唯一访客量。网站域名,后面你会看到,是捏造的。你只会在数据集中找到日期、地点和访客数量。
脸书先知有它的优势,虽然它不是特别健壮。但在预测方面,它简单、易用、全自动、快速。你不必处理数据维度,也不需要反向标准化。如果你不太熟悉 Python 和编码,并且你没有太多的时间建模,那么在我看来,这绝对是最好的选择之一。
在本文中,我将介绍,
但是,原文太长了。因此它现在被分成两部分——前 3 节在第 1 部分中,而第 4 节和第 5 节在第 2 部分中。如果您对安装和简单预测不感兴趣,请点击此处进入第 2 部分。
1。安装
几年前,当 python 还处于 3.8 版本时,新的 Prophet 用户只需 pip 安装库,然后进行预测。但是,尽管大多数 Python 用户已经更新到 3.9x,甚至更高的版本,但 Prophet 遗憾的是更新不快,只支持 3.8x 或更低的 Python 版本。因此,我们大多数人都需要一个解决这个不相容问题的方法。
虚拟环境是解决这一问题的方法之一。要了解什么是虚拟环境,请点击此处。
如果你只对 FB 先知感兴趣,请随意跳过。
- 要在 Windows 10 中创建一个虚拟环境(本教程是用 Windows 10 平台编写的,而不是我曾经工作过的 Mac),我们首先必须安装 Anaconda。请点击这里进入官方网站。
按照安装指南完成 Anaconda 安装。作者图片
然后按照安装指南完成安装。安装很容易。我们只需点击“是,是……”就能得到我们需要的东西。
**2。**安装完成后,我们将使用命令行提示。但是这一次,我们没有使用 Windows 命令提示符。相反,我们将使用 Conda 命令提示符。
在这篇 Prophet 教程中,我们将使用 Anaconda 命令提示符。上面画圈的是 Anaconda 提示符。作者图片
如果您找不到 Anaconda 命令提示符,请转到上面红色箭头所指的搜索栏,搜索“Anaconda 提示符”。
**3。**会弹出一个小黑窗口。在这个 Anaconda 命令提示符下,输入:
conda create -n python_3_8 python=3.8
它所做的是创建一个名为 python_3_8 的新工作环境,其中安装了 python3.8。
**4。**然后我们进入:
conda activate python_3_8
输入该命令后,系统将进入虚拟环境 python_3_8。从现在开始,您的 python_3_8 虚拟环境将取代您原来的 python 环境(暂时)。您也可以选择它作为 Visual Studio 代码中的工作内核。
要在完成项目后逃离这个虚拟环境,只需输入:
conda deactivate
**5。**现在我们有了工作环境,我们需要安装库。下面是我们将需要的库和依赖项。只需逐行安装下面的代码。
conda install libpython m2w64-toolchain -c msys2conda install numpy cython -c conda-forgeconda install matplotlib scipy pandas -c conda-forgeconda install pystan -c conda-forgeconda install -c anaconda ephemconda install -c anaconda scikit-learnconda install -c conda-forge seabornconda install -c plotly plotlyconda install -c conda-forge optunaconda install -c conda-forge Prophet
安装完所有这些之后,我们现在可以开始我们的数据了。
2.简单的 ETL 和数据可视化
我们将从 csv 加载数据,并在此显示数据。因为数据是从其他数据库中提取的,所以我不会在这里介绍这些步骤。因此,我们将直接转到 csv 文件并探索。数据可以在这里下载。
Jupyter 笔记本文件也可以在这个链接中找到— 下载笔记本。
首先,我们导入数据。
df = pd.read_csv(‘data.csv’)df2 = df.copy()
df2.head()
我们探索我们的数据。
网站独特访问者的数据框架。国家一栏是网站的服务区域。作者图片
print(df2[‘Country’].value_counts(), “\n”)print(df2[‘Country’].nunique(), “unique values.”)
德国有 23563 个数据点。作者图片
大致了解数据集后,我们清理 DateTime 列,只过滤需要的数据点。我只对德国的数据感兴趣,所以我将使用 loc 进行过滤。
df2[‘date’] = pd.to_datetime(df2[‘datepart’], dayfirst=True).dt.datedf2 = df2.loc[(df2[‘Country’]==’Germany’)] # we are only interested in the visitors from Germany in this tutorial.df_de = df2.copy()
现在我们有了一个数据框,里面只有网站在德国的表现。
确保数据集中没有空值。
df_de.isna().count()/df_de.count()
1.0 表示 100%,没有缺失数据点。作者图片
Prophet 对要馈入的 df 有严格的要求。 ds 和’y列是需要的标准列。其他的是,例如,“上限”和“下限”。我们在教程中不需要它们,因为这次我们将把模型设置为“线性”而不是“逻辑”。我将在本教程的第 2 部分中解释原因。
df_de2 = df_de.groupby(“date”).agg(np.sum)df_de2 = df_de2.reset_index()df_de2.columns = [‘ds’, ‘y’]df_de2 = df_de2[[‘y’, ‘ds’]]df_de2
“y”是要预测的目标值,“ds”是日期。它们是先知模型的两个基本要素。作者图片
然后,我将使用 Plotly 可视化我们的唯一访问者列的数据分布。Plotly 是我最喜欢的可视化库,因为它易于应用和交互。
import plotly.io as piopio.renderers.default = “notebook”fig_line = px.line(df_de2, x=”ds”, y=”y”, title=’The number of unique visitors of [www.lorentzyeung.com](http://www.lorentzyeung.com) in the previous 3 years’)fig_line.show()
在过去的 3 年里,www.lorentzyeung.com的独立访客数量。域名是捏造的。作者图片
用均值和标准差找出可能的异常值。
df_stat = df_de2.describe()mean = df_stat.loc[“mean”][“y”]std = df_stat.loc[“std”][“y”]upper = mean+std*3lower = mean-std*3print(‘ Mean: ‘, mean, ‘\n’, ‘Standard Deviation: ‘, std, ‘\n’, ‘Upper Limit: ‘, upper, ‘\n’, ‘Lower Limit:’, lower)
现在我们清楚地知道均值和异常值在哪里。只是我习惯用 3 个标准差作为上下大纲图截止线。作者图片
然后我们用方框图一点一点地显示可能的异常值。
我们的数据集中没有异常值。作者图片
我们的数据集几乎显示了一个完全对称的钟形(垂直),这意味着发现了正态分布。我们的数据集中没有异常值。这是理想的,因为正态分布是技术市场分析和其他类型的统计分析中最常见的分布类型。
3。简单预测(默认设置的预测)
在这个环节中,我将从一个简单的预言开始。这很简单,因为我们不需要在这里指定任何特定的参数值。我将只使用默认设置。然后,我们将观想我们的结果。
m = Prophet(interval_width=0.95, weekly_seasonality=False, daily_seasonality=False)m.add_country_holidays(country_name=’DE’)m.fit(df_de2)future_df = m.make_future_dataframe(periods=52,freq=’W’)forecast_default = m.predict(future_df)plot_forecast = m.plot(forecast_default)
在 Prophet 中直观显示原始数据点和预测线非常简单。我们的网站前几年一直在改进,甚至在疫情时代也是如此。但奥米克隆似乎带走了势头,扭转了趋势。作者图片
这个预测和图表对人脑有意义,至少对我的大脑有意义。尽管趋势趋于平稳,但 5 %的置信区间告诉我们,未来一年可能会呈下降趋势。
plt_components = m.plot_components(forecast_default)
组件图。作者图片
从上面的组件图中,我们可以看到我们的访问者数量呈上升趋势,尽管假期影响了性能。似乎圣诞节是我们网站最大的障碍。然而,一月和五月的银行假日会给我们的网站带来最强劲的增长。这里的图表完全有意义,因为你的网站销售的产品与圣诞节无关。
现在,您已经学习了如何安装和预测开箱即用的 Prophet 模型。在接下来的第二部分中,我将展示如何评估该模型,以及如何改进/优化它。
感谢您的阅读。如果你喜欢这个教程,请分享给你的数据科学朋友,还有关注我。以下是我继续为社区做贡献的动力。
请继续阅读文章的第二部分。
参考:
https://facebook.github.io/prophet/
您可能还喜欢:
用聚类算法重组体育联盟
原文:https://towardsdatascience.com/realigning-sports-leagues-with-a-clustering-algorithm-d6e9de9294d0
使用改进的 K-means 算法将职业运动队分组为最小化旅行距离的组
米克·豪普特在 Unsplash 上的照片
职业体育联盟的赛区结构解释
在美国,职业体育联盟,例如国家橄榄球联盟(NFL)、国家篮球协会(NBA)、职业棒球联盟(MLB)、国家曲棍球联盟(NHL)和职业足球联盟(MLS)将球队分成不同的组。属于同一个赛区的球队之间的比赛更频繁。这种分区结构被用来决定季后赛的参与,并滋生了激烈的竞争,因为经常在竞争中对抗的球队之间的敌意自然增长。然而,最重要的是,分区结构的作用是限制一支球队在一个赛季中为进行预定比赛而必须行进的距离。长途旅行会导致玩家疲劳、碳排放和过多的费用。理想情况下,一个赛区将由地理位置非常接近的球队组成,这样球队就不需要走很远的路去接触他们最频繁的对手。
重组,将球队组合成一个新的分区结构的过程,并不经常进行,但随着联盟期待在新的城市增加新的球队,T2 重新安置现有的球队,这可能很快就有必要了。如果联赛想要重新调整级别以最小化旅行距离,在级别数量保持不变的情况下,球队应该如何分组?此外,这些假设的最优划分与目前使用的划分相比如何?
约束 K-均值聚类算法
形成团队组以最小化每个组中团队之间的距离可以被视为聚类问题。K-means 算法可能特别适合,因为它寻求最小化每个点和其相关聚类中心之间的平方距离之和。然而,标准的 K 均值算法是不够的。运动分区的大小大致相同,标准的 K-means 算法不能保证得到的聚类具有这种性质。因此,我们使用一个受约束的 K-means 聚类算法,该算法将聚类数、最小聚类大小和最大聚类大小作为参数。这种改进的算法依赖于线性规划和网络流的思想。我们使用由 Joshua Levy-Kramer 创建的算法实现,其文档可以在这里找到。在这篇文章中可以找到该算法的高级概述,而在原始论文中提供了详细描述。
定义团队之间的距离
我们使用的算法实现依赖于笛卡尔坐标系中定义的欧几里德距离。因此,我们必须在笛卡尔坐标中定义各个团队的位置。为了做到这一点,我们将首先获得每个球队主场的经度和纬度。这些信息来自维基百科,可以在 Kaggle 上找到。
数据集|作者图片中的行示例
然后使用一些巧妙的三角学(这里描述为并在下面的函数中实现),我们将把体育场的位置转换成笛卡尔坐标,然后以有意义的方式输入到我们的算法中。
函数将纬度、经度点转换到笛卡尔坐标系
执行重新调整的代码
随着方法的确立,我们现在准备开始重新调整。我们首先导入必要的库并执行一些初步的数据操作。
接下来,我们将使用受约束的 K-means 算法将每个联盟中的球队分配到新的赛区。联盟和相关联的新分区分配作为键值对存储在字典中,以供以后使用。
最后,为了可视化原始的分区结构和新的重新排列的结构,我们使用了 plotly 包中的 Scattergeo 功能。
比较当前部门和重新调整的部门
我们将首先看一看美国职业棒球大联盟(MLB),它由 6 个分部组成,每个分部由 5 个队组成。请注意,一些 MLB 体育场,如芝加哥的 Wrigley Field 和 Guaranteed Rate Field,彼此距离很近,这使得标记很难在地图上分辨。
(左)目前的 MLB 部门结构|(右)重新调整的 MLB 部门结构|图片由作者提供
在上表中,我们放弃为重新调整的部门分配名称,因为在当前分组和重新调整的分组之间不一定存在一一对应关系。
接下来,我们考察 NFL 部门在重组后是如何变化的。NFL 分为 8 个分部,每个分部由 4 支球队组成。体育场比球队少,因为索菲体育场由洛杉矶充电器和洛杉矶公羊队共用,而大都会人寿体育场由纽约巨人队和纽约喷气机队共用。正如人们所料,那些共用一个体育场的球队在重新组合后总是被分在同一个组。
(左)当前的 NFL 部门结构|(右)重新调整的 NFL 部门结构|作者图片
NHL 分为 4 个赛区,每个赛区有 8 支球队。新泽西魔鬼队、纽约流浪者队和纽约岛民队的主场非常接近,使得标记很难在地图上分辨。
(左)当前的 NHL 部门结构|(右)重新调整的 NHL 部门结构|作者图片
NBA 和 MLS 都分成两个部分。NBA 的 30 支球队平分秋色,东部赛区 15 支,西部赛区 15 支。MLS 东部联盟有 14 支球队,而西部联盟有 13 支球队。正如下面的地图所强调的,两个联赛的重新划分与他们现在的形式是一样的。
NBA 分区结构|作者图片
MLS 部门结构|作者图片
评估行驶距离的改善
最后,我们将分析通过采用我们重新调整的部门可以节省多少出行距离。为此,我们将确定每个组别的球队之间的平均距离。例如,美联东区的 MLB 球队平均相距 602 英里。然后,对于每个联盟,我们将对当前分区的值和重新调整的分区的值进行平均。这将为每个联赛产生两个数字,分别测量在当前分区结构和我们重新调整的分区结构下球队与分区间对手的平均距离。为了实现这个过程,我们首先需要定义一个函数来返回两个纬度和经度点之间的距离(以英里为单位)。这可以通过这里描述的哈弗辛公式完成,并在下面的函数中实现。
函数查找两个纬度、经度点之间的距离(以英里为单位)
然后,我们可以使用下面的代码计算并打印每个联盟的两个最终平均值。
在目前的分区结构下,MLB 车队与同级别其他车队的平均差距为 608 英里,而在重新调整后的分区结构下,这一差距降至 436 英里。如下表所示,NFL 和 NHL 的距离也有类似的缩短。
结论
虽然计算最优的重组部门只是一个简单的数学问题,但实施它们需要利益相关者之间的巨大合作。许多球迷、球员和团队人员无疑会引用长期部门竞争的损失作为稍微短的部门间旅行的高价格。一些人可能会说,如此激烈的重新调整会让游戏失去其“历史”和“文化”的一个基本部分,更不用说安排竞争游戏以提高收视率的机会了。然而,积极的影响将是可以衡量的和直接的。与团队交通相关的碳排放和费用将会减少,与长途旅行相关的球员疲劳也会减少。这些好处会随着重新划分的比赛和赛季数量的增加而积累。虽然一些旧的部门竞争将会失败,但这些比赛仍然会发生,因为只有一小部分比赛是针对部门间的对手。新的竞争将会随着重新组合的队伍开始频繁地相互竞争而发展。
从更广阔的角度来看,人们可以想象,用于重新排列划分的数学方法可以很容易地应用于其他地理聚类问题。例如,给定 12 个商店位置和 3 个区域经理,公司可以使用约束 K-means 聚类算法将经理最优地分配到 4 个商店的组中。
参考
[1] J .利维-克雷默,k-均值约束 (2020)
每个人都应该参加数据科学课程的 5 个理由
意见
为什么您应该了解数据及其科学
图片来自 Unsplash 。
数据科学目前是一个热门话题,你可以认为这是一个暂时的炒作——或者不是,这取决于你的观点。无论你持何种观点,有一件事是肯定的:数据科学是在这里停留。而且到处都会是**。**
【1920 年至 2019 年“数据科学”的谷歌 Ngram 浏览器。数据来自谷歌,图片由作者提供。
那么,是什么让它如此受欢迎,为什么如此突然?“数据科学”兴起的一些原因可以总结为三个要点,上图也描述了谷歌 Ngram 查看器工具:
- 过去几年中计算能力的提高****
- 算法和框架的各种突破和发展****
- 收集了越来越多的数据——有一种需要利用这些数据的感觉
…时间会告诉我们更多数据科学作为一个领域如此重要的原因。
数据科学知识不仅对那些整天开发模型、处理数据、分析统计数据和编码的人有用。我建议每个人都去上一堂数据科学的课**,不管你从事什么行业。**
数据科学对每个人都有用。
为什么?
理由 1 |它让你了解统计学
统计学是数据科学的主要组成部分。数据科学中使用的许多流行方法在核心上依赖于经典的统计模型。参加数据科学课程会让你对统计学有一个基本的了解,通常会帮助你更好地理解描述、情况和含义。
相关性与因果性
你会明白相关性并不总是等于因果关系,你不能仅仅因为某些事件之间存在相关性就对它们做出假设。因果关系意味着一件事引起另一件事**,而相关性只是两个事件之间的简单关系,这两个事件将**相互联系起来。****
我们常说“相关性并不意味着因果关系”。
一个简单的例子可能是:我们通常观察到,当天气温暖和阳光充足时,冰淇淋的销售和鲨鱼攻击的数量会增加。但是,一个****不会导致另一个。它们与有关,因为好天气会让更多的人去海滩吃冰淇淋,去海里游泳,这反过来会增加鲨鱼袭击的可能性。****
在你的日常生活中,这将帮助你更好地理解情况和分析索赔。这将使你更有能力区分好的论点和错误的论点,并帮助你向你的同伴阐述有效和正确的论点。
平均值对中位数
理解平均值 和中位数之间的差异也是通常被低估的事情。人们倾向于更频繁地使用平均数而不是中位数。然而,均值可以隐藏数据上的信息,只有通过同时查看中值我们才能看到这些信息。
平均值是所有数据点的平均值**-中值将数据分成一半,即 50%的点高于中值,50%的点位于中值。**
举个例子,让我们来看看下面的数据集 A 和 B,它们是关于人们的薪水的:
具有平均值和中值的两个数据集的示例。图片作者。
我们可以看到,A 组的平均值比中间值低得多。对于 B 组,我们将数据集中的一个值改为一个比 A 组大得多的数字:我们立即可以看到平均值是如何增加的,而中位数保持不变。突然,我们意识到平均值对于这组数据来说是一个很差的指示,因为它对极端值非常敏感。
在你的日常生活中,这将帮助你在更好地解释数据**,并且当你有数据呈现给你时,会让你更加 怀疑。它会让你明白数据不仅仅意味着**,还会让你成为一个更 可信和精确 展示者,以防你自己也要展示数据。****
…还有更多!
理解统计学的基础知识会给你带来更多的好处,上面两个只是一小部分,是它在你日常生活中潜在价值的非常基本的例子。
理由 2 |它让你了解数据
这是相当类似的,并与原因 1 携手并进。没有统计学知识,你很难很好地理解数据。当我开始熟悉数据时,我意识到的主要事情之一是,你可以更好地理解围绕它的通用工具和技术。
我的意思是:
什么是数据?
您将了解数据可以以何种形式和类型进入:从结构化数据到非结构化数据。从定量和定性值,到名义值、序数、连续值和离散值。
你知道数据如何存储以及存储在哪里,举几个例子:
- CSV、XML 和 XLS 文件,以及纯文本文件
- 关系和非关系数据库
- 二进制数据,比如图像。
您还将了解如何访问数据:我们可以在编程语言(f. e. R 或 Python)的帮助下读取 CSV 文件,或者用 SQL 语言直接从数据库中查询关系数据。
你了解数据的 4 个(甚至到 7 个 ) V:量、种类、准确性、速度等。
在日常生活中,这不仅会让你对 IT 有一个很好的总体了解,而且对于你可能正在进行的一些 IT 相关的对话和小型会谈来说,这也是很方便的知识。
我如何显示数据?
数据本身是第一步,解释和可视化是下一步。
大多数基础数据科学课程都会涉及到数据可视化的主题,很快你就会听到这样一句话:
“最好不要使用饼图来可视化数据.”
一开始你可能会想:为什么是?但是你看到的例子越多,你就会越意识到其中的道理,很快你可能会提倡自己不要使用饼状图。
条形图与饼图比较。图片作者。
在上面的图片中,我们有一个很好的例子来说明为什么饼状图(一般来说)比 f. e .条形图更不利于可视化。众所周知,人眼很难理解和估计角度,尤其是 T2,饼图的碎片越多。如果没有对饼图进行注释,您会正确估计“借记卡”和“数字钱包”组值之间的差异吗?也许吧,但阅读条形图通常会更容易、更清晰,因为它让我们可以轻松地比较哪怕是最细微的数值差异。
在你的日常生活中,这会让你明白,你应该对你对饼状图的解读持保留态度,因为这些可能会误导你。这也将使你创造更好的演示,并成为你的数据的更有影响力的演示者。
理由 3 |你洞察编程逻辑
几乎在数据科学的每一门课中,你都可能需要学习一些编程的入门课程。这对你来说可能听起来很可怕——相信我,一点也不可怕。你不必为了从中获益而将你的编程技能提升到专家水平。
在我上第一堂数据科学课之前,我从未做过任何编程。所以当然,这是相当艰难的——但是你开始学习的每一件新东西在开始时都是艰难的。问题是:你会坚持学多久?或者说,你会有多一致?不管你会坚持多久,对编程有一点基本的了解已经有它的好处了。我的意思是:
透过更符合逻辑的镜头看问题
首先,你会开始有一种感觉,觉得思考更有逻辑,更有计划性。它将帮助你通过一个更符合逻辑的视角来看待这个世界。我们现代世界的大多数解决方案都是基于技术的,并且已经以某种方式被编程。你将会被最终理解这些事情是如何潜在工作的启发,高层次的。
自动完成自己的任务
更进一步,你甚至可以开始做你自己的小编程项目。你可以开始写你自己的脚本和自动化日常任务。听起来很酷,不是吗?我可以确认:是。
我给自己写了一堆脚本来自动化某些时间密集型任务,比如 f. e .复制粘贴或者抓取内容,或者在我的电脑上寻找重复的图像。看到这些项目在几个小时的工作后变成现实,会带来一种令人满足的感觉。
你现在可能会问自己从哪种编程语言开始。在我看来,几乎任何一种编程语言都会给你一个更符合逻辑的世界观。然而,有些语言比其他语言更适合某些任务,也比其他语言更灵活。我个人从 R 开始,继续(并留在)Python,我研究了 SQL、Matlab 和 Java。
说到易用性和对数据科学相关内容的支持,我绝对可以推荐从 Python 开始。
理由 4|你知道人们谈论什么
你可能会遇到这样的情况:你与人交谈,然后关于数据科学的对话出现了。现在你将成为这次谈话的一部分,因为你对谈话的内容有了基本的了解。这可能会帮助你进行更有意义的谈话,让你比以前更容易与一些人或团队建立联系。
无论你身处哪个行业,你对数据科学的理解都会让你更加博学,并让你有机会成为团队之间的纽带——这个人既了解故事的一面,也了解故事的数据科学部分。
你可能会读到一篇关于一些人工智能算法接管世界的文章,或者一种新开发的预测算法的一些突破:现在你会对有一个简单的了解,这是由组成的。当然还不详细,但是足够让你理解这篇文章可能包含的内容的含义。
在日常生活中,人与人之间的社会联系以及跟上技术发展是我们未来的两个非常重要的部分。技术无处不在,不会少。有必要了解每天围绕着我们的。****
理由 5|你理解其中的含义
上过几堂数据科学的入门课后,你会更好地理解为什么数据如此有价值**,你能用它实现什么,以及为什么在线广告业务如此成功。**
你会明白,数据科学中的技术是伟大的,可以用 做好 ,但那个数据也可以是关于**,f. e. 有偏差的时候。可能还有关于数据伦理的担忧,或者关于某个算法缺乏可解释性的担忧。**
在日常生活中,这将让你很好地理解为什么会有这样的潜力**,而且还有关于数据的关注。你将开始问自己更多关于你自己的数据和数据隐私的问题。**
结论性理由
总而言之,在你上完第一堂数据科学课后,你将能够更好地与数据科学家的工作联系起来,现在你知道了他们的一些技巧:你将区分相关性与因果关系**,,将开始利用中位数,而你将尽量避免使用饼状图。最终,你会更擅长解读数据,并提供更好的演示。**
我亲身经历了阅读数据科学的许多好处,因此我强烈推荐任何人参加入门课程。你不必成为一个专家,只是一个基本的了解已经将是非常有益的。希望你也这么想!🎉
欢迎在评论中与我分享您的反馈!💬
下面你会发现几个链接,我个人觉得在开始阅读和学习数据科学的时候非常有用。
建议和参考:
[1]Coursera.com,IBM,数据科学简介 (2022)(优秀的入门级课程,从基础开始)
[2]Coursera.com,深度学习。AI &斯坦福大学,机器学习 (2022)(令人惊讶,但相当高级的课程,深入细节)
[3] 关于 Medium.com 的数据科学(你经常会在这里找到非常好的有教育意义的文章)
食谱烹饪分类
原文:https://towardsdatascience.com/recipe-cuisine-classification-278ea0837c94
伯特变压器&食物-药物负相互作用
在 Unsplash 上由S O C I A L C U T拍摄
介绍
几个模型架构可以用来执行烹饪分类。一些最受欢迎的模型,按照复杂程度的增加顺序,是支持向量机(Pouladzadeh 等人,2015 年)、BERT (Devlin 等人,2018 年)、RoBERTa(刘等人,2019 年)或 3(布朗等人,2020 年)模型。
变形金刚(电影名)
与计算机视觉不同,在自然语言处理(NLP)中,预训练模型最近才变得广泛可用。部分由于文本数据集的稀缺,NLP 的进展缓慢,直到变压器(BERT)的双向编码器表示的发布(Devlin 等人,2018)。
BERT 有两种预训练模型:基本模型和大型模型。虽然两者使用相同的架构,但前者包含 1.1 亿个参数,而后者包含 3.45 亿个参数。能够使用预先训练的模型并对其进行微调以适应不同的任务意味着即使在缺乏足够的数据来训练具有数百万个参数的模型的情况下,也可以避免模型过度拟合。较大的模型通常具有更好的性能(Devlin 等人,2018 年)。
在微调 BERT 时,有三种可能性(Devlin et al .,2018):
- 训练所有建筑
- 训练一些层,冷冻其他层
- 冻结整个网络并将额外的层附加到模型上
与其他神经网络架构一样,提供的训练数据量越大,它的性能就越好。因此,一个重要的收获是,运行更多代的 BERT 将导致更高的准确性,并有足够的数据。就所需的计算能力而言,它相当于使用多个最先进的 GPU 进行几天的训练(Devlin et al .,2018)。
BERT 的另一个特点是它的双向方法。相比之下,以前的努力要么从左到右要么结合从左到右和从右到左的训练来查看文本序列(Devlin 等人,2018 年)。
与递归神经网络和长短期记忆神经网络相比,BERT 的一个优点是它可以并行化。这意味着它可以通过在多个 GPU 中训练来加速。如果输入数据是文本序列,这意味着它可以一次接受多个标记作为输入(Devlin et al .,2018)。
变形金刚的两个后验实现分别是 Research 和 OpenAI 的鲁棒优化 BERT 预训练方法(RoBERTa)(刘等,2019)和生成预训练变形金刚 3(3)(布朗等,2020)。
BERT transformer 用于根据配料列表构建菜肴预测模型。这使得在烹饪水平上用于治疗 AD 的药物的负面食物-药物相互作用最小化。
烹饪/药物的负面相互作用
因为 BERT 模型以前是用数百万个样本训练的,所以它们可以通过更少的微调数据被广泛的应用程序重新调整用途(Devlin 等人,2018)。在这种特定情况下,烹饪预测。
配料和食谱的选择受地理和文化的影响很大。一些食谱比其他食谱更富含已知 AD 和/或新冠肺炎(Laponogov 等人,2021 年)打浆特性的成分。在烹饪水平上考虑这种变化允许我们在烹饪水平上预测最有可能减少与药物的负面相互作用的数量,即使没有所有成分的这种信息。食物-药物相互作用可导致药物生物利用度的显著降低,这是由于食物分子和药物化学成分之间的直接相互作用,或者是由于食物摄入的生理反应。世界上销售最多的一些药物是抗肿瘤药和针对神经系统疾病的药物。图 1 中提供的信息有助于我们根据食谱估算食谱的健康益处(Jovanovik 等人,2015 年)。
图 1 药物(左图)和菜肴(上图)之间现有相互作用的百分比。作为参考,对于北美料理()和抗肿瘤药物( A ),预计 0.005288%的负相互作用。[图片来源=(约万诺维克等人,2015)]
另一方面,与其他行业一样,推荐系统通常会将产品分为多个类别。食物推荐系统可以通过将用户导向他们最喜欢的选择而受益于美食分类(Anderson 等人,2018)。
一个烹饪分类器被训练、测试和验证,以预测神经(AD)和抗感染(新冠肺炎)药物与烹饪确定的食谱之间的负面相互作用。
方法
使用 BERT 变换器,训练、验证和测试了一个根据配料列表预测菜肴的模型。
Kaggle 和 Nature 数据集包含大约 100000 个配方,用于训练变压器。每个食谱都包括一份配料清单,以及相应的菜肴。一份按字母顺序排列的配料清单给了模型。数据集的 80%用于训练,10%用于验证,10%用于测试。
在训练模型之前,使用 BERT 记号化器,记号化步骤将每个配方分成 50 个记号。初始变压器的所有参数都被冻结。
为神经网络选择的体系结构包括一个下降层,一个包含 768 层 512 个节点的密集层 1 和一个包含 512 层的层 2,每个层有 11 个节点(与菜肴的数量相同)。
在训练时,使用了实现权重衰减为 1e-3 的 Adam 算法的优化器。负对数似然损失被用作损失函数。并且模型收敛需要 40 个时期。训练是在 Google Collab 的一个 GPU 中进行的。
模型的性能是通过对每种菜肴的精确度、召回率和 f1 分数来评估的。
最后,介绍了在抗感染类(针对新冠肺炎)和神经类(针对 AD)中具有最低数量的负面食物-药物相互作用的烹饪。
结果和讨论
菜肴分类
在图 2 中, Kaggle & Nature 数据集基于配料数量的食谱数量分布被表示。
图 2 配方分布在 Kaggle & Nature 数据集中对应的配料数量
根据上图,Kaggle 和 Nature 数据集中的大多数食谱包含 6 到 8 种成分。平均来说,每份食谱有 12 种配料。没有一个食谱包含超过 60 种成分。
在图 3 中,显示了 Kaggle & Nature 数据集中每种菜肴的食谱数量。
图 3 在 Kaggle & Nature 数据集中每个菜系的菜谱数量。
共有 96250 种食谱,涵盖 11 大菜系。食谱最多的三大菜系分别是北美、南欧和拉美,分别有 45843、14178 和 11892 种食谱。
在表 1 中,展示了菜肴分类器精度、召回率和 F1 分数。
表 1 模型分类准确度——精度、召回率和 f1-分数被详细描述到训练数据集中的每一个菜系
南欧、拉丁美洲、北美和东亚的 F1 得分高于 55%。
图 4 显示了与神经系统药物(AD)和抗感染药物(新冠肺炎)的负面食物-药物相互作用最少的菜肴的地理分布。
图 4 拉丁美洲美食与神经类药物——AD 的负面相互作用数量最低。西欧抗感染药效果最好——新冠肺炎。
拉丁美洲和西欧的菜肴分别显示出与神经系统药物和抗感染药物的负面相互作用最少。
Kaggle & Nature 数据库包含分布在各大菜系的不平衡数量的食谱。虽然 BERT 模型是针对不平衡数据集进行训练的,但这可能仍然会影响模型的预测能力。只考虑成分列表而不考虑各自的食谱说明可能会导致较差的检测。以及北欧某些菜系的食谱数据集的有限大小。某些菜系的精确度、召回率和 f1 评分较低,可能与缺乏独特的配料有关。
后续步骤
为了实现训练更准确的烹饪分类器的目标,我们将优化电流互感器的架构(考虑罗伯塔和 GPT-3 的实现),并考虑烹饪过程,而不仅仅是配料。使用带有更多食谱的烹饪标签数据集也将有助于在 11 种烹饪中获得更高的检测率。
参考
Pouladzadeh,p .,Shirmohammadi,s .,Bakirov,a .,Bulut,a .,和 Yassine,A. (2015 年)。基于云的食品分类 SVM。多媒体工具及应用, 74 (14),5243–5260。https://doi.org/10.1007/s11042-014-2116-x
Devlin,j .,Chang,m-w .,Lee,k .,& Toutanova,K. (2018 年)。 BERT:用于语言理解的深度双向转换器的预训练。
刘,y .,奥特,m .,戈亚尔,n .,杜,j .,乔希,m .,陈,d .,利维,o .,刘易斯,m .,泽特勒莫耶,l .,&斯托扬诺夫,V. (2019)。 RoBERTa:一种稳健优化的 BERT 预训练方法。
Brown,T. B .、Mann,b .、Ryder,n .、Subbiah,m .、Kaplan,j .、Dhariwal,p .、Neelakantan,a .、Shyam,p .、Sastry,g .、Askell,a .、Agarwal,s .、Herbert-Voss,a .、Krueger,g .、Henighan,t .、Child,r .、Ramesh,a .、Ziegler,D. M .、Wu,j .、Winter,c .……Amodei,D. (2020 年)。语言模型是一次性学习者。
*Laponogov,I .,Gonzalez,g .,Shepherd,m .,Qureshi,a .,Veselkov,d .,Charkoftaki,g .,瓦西利乌,v .,Youssef,j .,Mirnezami,r .,Bronstein,m .,和 Veselkov,K. (2021)。网络机器学习绘制富含植物化学成分的“超级食物”来对抗新冠肺炎。人类基因组学, 15 (1),1。【https://doi.org/10.1186/s40246-020-00297-x *
Jovanovik,m .,Bogojeska,a .,Trajanov,d .,和 Kocarev,L. (2015 年)。使用关联数据方法推断烹饪-药物相互作用。科学报道, 5 (1),9346。https://doi.org/10.1038/srep09346
用机器学习识别排球比赛阶段
原文:https://towardsdatascience.com/recognize-volleyball-game-stages-with-machine-learning-f7af06c414b2
使用人物探测器对玩家位置进行分类
排球比赛阶段
用人工智能分析排球比赛有多种方法。我曾经跟踪球是为了把游戏时间从整个视频中分离出来。
另一种方法是确定游戏阶段,并根据阶段流程做出决策。
让我们假设排球比赛包括 4 个阶段:
- 没有游戏。没有人或几个人在院子里闲逛。
- 静止位置。上菜前人们都是站着不动的。
- 游戏。
- 欢呼。即使拉力赛输了,聚在一起互相加油也是很常见的。
所有这些阶段都因人们相对于法庭的位置和彼此的位置而不同。这意味着我们首先要做的是找到照片中的人。
人物检测
我在以前的文章中对的最佳探测器做了一些研究,因此我将使用 MobileSSD 来实现这个目的。
人物探测器生成带有玩家坐标的盒子。该信息将是阶段分类模型的输入。
有时探测器会漏掉人,这给数据增加了一些噪声。
数据准备
出于训练目的,需要手动对图片进行分类,将它们移动到 4 个不同的文件夹中。
然后是一个很好的问题,关于如何表示机器学习的输入数据。有两个主要选项:
- 使用检测到的框的数字表示
- 使用图片
我决定使用图片选项,因为:
- 输入大小应该是稳定的,但是我们事先不知道检测的次数。球场上可能有多达 12 名球员,但球迷、裁判和替补队员可能会增加这个数字。图片大小是固定的。
- 检测是无序的。为了将它们用作数组输入,我们需要以某种方式对它们进行排序,例如,从左到右、按大小排序等等,但哪种选择更好并不明显。对于图片,我们按原样使用检测。
- 人性化。看着箱子上的数字,很难说现在是什么阶段。一张图片提供了很好的线索。
为了将检测结果表示为输入图片,我将它们绘制为黑色背景上的白色实心矩形,大小调整为 64x64。
def get_mask(boxes):
pic = np.zeros((H,W,1), np.uint8)
clr = 255
for r in boxes:
cv.rectangle(pic, (r[0], r[1]), (r[0] + r[2], r[1] + r[3]), clr, thickness=-1)
pic = cv.resize(pic, (64, 64))
return pic
然后我为每个阶段准备了面具:
欢呼
不休息
玩
静止位置
选择分类方法
多标签分类有多种方式,所以选择并不容易。最后,我选择了两个最基本也是最流行的方法:
- 从 sklearn 最近的邻居(又名 KNN)
- 带 Tensorflow-Keras 的简单神经网络(又名 TFK)
KNN 在相同数据上显示 81%的准确率,在随机样本上显示 66%的准确率。
TFK 也稳定在 80%左右。
测试分类
我们来挑选一个测试排球的视频。我使用了由格拉茨计算机视觉研究所提供的奥地利业余联盟的公开视频集。
- 从游戏中提取帧。人们移动得不是很快,所以每秒 2 帧可能就足够了:
ffmpeg -i video.mp4 -r 2 frames/%05d.jpg
2.对帧运行 MobileSSD 检测器并生成 json 文件。
def detect_pic(ssd, img, thr = 0.3):
rows = img.shape[0]
cols = img.shape[1] ssd.setInput(cv.dnn.blobFromImage(img, 1.0/127.5, (600, 600), (127.5, 127.5, 127.5), swapRB=True, crop=False))
out = ssd.forward()
r = np.array([cols, rows, cols, rows]) boxes = []
scores = []
for d in out[0,0,:,:]:
score = float(d[2])
cls = int(d[1])
if cls != 1:
continue
if score < thr:
continue box = d[3:7] * r
box[2] -= box[0]
box[3] -= box[1]
boxes.append(box.astype("int"))
scores.append(score) if len(boxes) < 1:
return [] dxs = cv.dnn.NMSBoxes(boxes, scores, thr, 0.1)
return [boxes[i].tolist() for i in dxs.flatten()]
3.将检测转换成输入掩码。
这两种方法的表现都比训练时差:
- KNN——72%
- TFK——70%
5.从阶段预测中建立拉力
令人惊讶的是,就反弹的可靠性而言,即使是 KNN 也略胜 TFK,TFK 表现得更好:
- KNN 认可了 29 次集会中的 8 次
- TFK 承认了 20 次集会
结论
拉力赛削减是这项工作的最终目标,结果是有希望的。即使不够精确,TFK·卡特也能为视频编辑和观众节省一些时间。
削减反弹有多种原因:
- 密集的整场比赛录音。它使视频更小(约为原始的 30%),存储更便宜,加载更快,对观众来说更迷人。
- 将漫长的游戏分割成有意义的部分。这对球员、他们的父母和粉丝来说很重要——小的拉力赛片段可以很容易地分享或保存在个人照片中。
到目前为止,自动对倒可能比目前更精确,第一批结果是有希望的,能够产生一些有用的结果。
链接
- 移动探测器
- 最近邻分类器
- Tensorflow-Keras 分类器
- 代号为的 Github 回购
- 视频数据集
参考资料:
利用时空上下文改进体育活动识别
乔治·瓦尔特纳、托马斯·毛特纳和霍斯特·比朔夫
在 Proc。DVS-体育领域计算机科学会议(DVS/GSSS),2014 年
自动化体育游戏分析的室内活动检测与识别
乔治·瓦尔特纳、托马斯·毛特纳和霍斯特·比朔夫
在 Proc。奥地利模式识别协会(AAPR/OAGM)研讨会,2014 年
矩阵分解——奇异值分解(SVD)解释
原文:https://towardsdatascience.com/recommendation-system-matrix-factorization-svd-explained-c9a50d93e488
利用潜在因素推荐构建推荐系统管道
图片由 Vlado Paunovic 从 Unsplash 拍摄
本文将概述推荐系统矩阵分解的直觉和 Python 实现。以下是文章的提纲。
目录
- 矩阵分解背后的直觉
- 奇异值分解
-奇异值分解的数学
-示例演练 - 问题陈述
- 数据
-要求 - 解决方案架构
- SVD 推荐系统实现
-生成用户-项目矩阵
-计算 SVD
-生成推荐 - 挑战
- 结束语
- 资源
矩阵分解背后的直觉
推荐引擎是机器学习的一个子类,旨在为一些用户或项目提供评级。在推荐系统中,矩阵分解属于协同过滤的范畴。直观地说,协同过滤旨在基于与用户 A 相似的其他用户的交互来识别用户 A 喜欢的项目。您可以将此视为一个优化问题,我们的目标是找到一种方法来为给定的产品/用户产生最佳评级。
在众多公司和行业中使用的推荐引擎存在各种各样的问题。登录脸书/ Meta、Instagram、Twitter、Pinterest、亚马逊等时。该平台会根据你自己的历史以及与你相似的其他用户的历史,向你推荐帖子/项目。相似性是一个广义的术语,你可以被认为是相似的用户,因为你有相似的品味,追随相同的人,喜欢/互动相同的帖子和用户,等等。
矩阵分解背后的直觉相当简单,给定一些用户-项目矩阵,您希望分解该矩阵,这样您就有一个独立的用户矩阵和一个项目矩阵。这允许我们对原始矩阵的每个潜在因子应用不同的正则化。例如,当产品是歌曲时,因素可以测量说唱和国家之间的比较、歌曲被播放的次数与歌曲持续时间的计数等。下图说明了输入用户项目(歌曲)矩阵 m 的分解。
矩阵分解例子。图片由作者提供。
这种方法是由西蒙·芬克在 2006 年推广的,因为它在网飞挑战赛中广泛流行[1]。Funk SVD 是 Simon Funk 提出的算法名称。尽管 SVD(支持向量分解)名副其实,但并没有 SVD 技术应用于它[1]。
奇异值分解
奇异值分解的数学
给定一些输入矩阵 M,SVD 的公式可以概括如下:
奇异值分解公式(图片由作者提供)。
M : An m x n matrix which you want to decompose
U : An m x m complex unitary matrix (left singular vectors)
Σ : An m x n rectangular diagonal matrix (holds the eigenvalues)
V : An n x n complex unitary matrix (right singular vectors)
步骤 1:通过将矩阵 m 乘以它的转置:M*Mᵀ,将它转换成方阵
第二步:计算矩阵 M*Mᵀ.的特征值和特征向量这样做的结果将对应于σ和 U 矩阵。
线性变换的特征向量是一个非零向量,当对其应用线性变换时,它最多改变一个标量因子。相应的特征值(通常用λ表示)是本征向量缩放的因子。
-【4】https://en.wikipedia.org/wiki/Eigenvalues_and_eigenvectors
第三步:用下面的公式求解 v:v = 1/σmᵀ u
示例演练
我们来计算下面这个矩阵 m 的 SVD。
SVD 的输入矩阵。图片由作者提供
M*Mᵀ
将输入矩阵 M 乘以其转置矩阵
步骤 2:计算特征值和特征向量
你可以使用下面的计算器来计算相关的特征值和特征向量。下面的链接提供了如何计算这些值的分步指南。如果你对此不感兴趣,那么就用 numpy 来计算特征值和特征向量
M*Mᵀ.结果的相关特征值和特征向量图片由作者提供
取非零特征值的平方根,生成σ矩阵:
特征值的平方根。图片由作者提供。
特征值的平方根在对角线上的适马矩阵。图片由作者提供。
为了计算 U 矩阵,你必须计算每个特征向量方向的单位向量。下面的链接概述了如何计算单位向量。
特征向量 1 的单位向量:[1,2,1]。图片由作者提供。
特征向量 2 的单位向量:[1,-1,1]。图片由作者提供。
特征向量 3 的单位向量:[1,0,1]。图片由作者提供。
这就产生了最终的 U 矩阵。
从特征向量的单位向量计算的 u 矩阵。图片由作者提供。
第三步:通过公式求解 v:v = 1/σmᵀ u
V 的计算值。图片由作者提供。
您可以参考下面的参考资料,深入了解 SVD 背后的计算。
问题陈述
我们希望使用音乐数据集建立一个协同过滤推荐系统。生成用户项目矩阵并结合余弦相似度使用奇异值分解来识别用户可能感兴趣的歌曲。
数据
出于本教程的目的,我们将使用 pandas、numpy 和 random 来合成数据集。本节将提供一个脚本,该脚本将合成一个与音乐相关的数据集。该数据集将在以下部分中用于推荐系统的应用。本教程的目的不是获得有意义的结果,而是向用户展示奇异值分解背后的实现。因此,这些建议的结果将是无意义的,但这些方法将类似于工业生产级环境中的方法。
要求
python=3.8.8
pandas=1.2.4
numpy=1.20.1
运行该脚本后,您应该会看到一些类似于下图的示例数据(不精确,因为它是随机生成的)。
合成音乐数据。图片由作者提供。
解决方案架构
现在我们有了一个数据集,我们可以考虑如何解决这个问题。我们将从基于与监听次数相关的值生成用户条目矩阵开始。这种情况下的项目是 song _ ids,而用户对应于 user_id。接下来,我们可以使用 SVD 来计算相关的 U、σ和 V 矩阵。最后,我们可以使用余弦相似性来比较用户向量和项目向量,以确定用户感兴趣的前 N 个项目。
奇异值分解推荐系统的实现
生成用户-项目矩阵
计算奇异值分解
生成建议
挑战
在推荐系统中使用协同过滤方法会面临各种各样的挑战,其中最常见的是冷启动问题。冷启动问题指的是推荐引擎不能为新的项目/用户产生合适的预测。或者具有低交互量的用户/项目,本质上它生成的用户-项目矩阵非常稀疏。这是因为缺乏新产品或产品发布的信息。
源于冷启动问题的协同过滤的另一个常见问题是推荐的多样性。这种方法基于其他用户/产品的历史交互来推荐产品/用户,本质上意味着非常受欢迎的产品几乎总是比交互太少的其他产品更受推荐。
可扩展性是协同过滤算法的一个常见问题,随着用户、产品和交互的数量增加,用户-项目矩阵的大小也成比例地增加。当数据大量扩展时,这会导致问题,并且会增加计算时间。通常,处理较大数据集的代价是性能会提高,而速度会降低,反之亦然。
结束语
本文概述了矩阵分解背后的直觉、数学和实现,特别是奇异值分解(SVD)。我展示了如何使用 SVD 创建一个协同过滤推荐引擎,它使用了我们用 Python 合成的音乐数据集。
请在我的 GitHub 页面这里查看与本教程相关的资源库。
如果你想转型进入数据行业,并希望得到经验丰富的导师的指导和指引,那么你可能想看看最敏锐的头脑。Sharpest Minds 是一个导师平台,导师(他们是经验丰富的实践数据科学家、机器学习工程师、研究科学家、首席技术官等。)将有助于你的发展和学习在数据领域找到一份工作。在这里查看。
资源
- [1]https://en . Wikipedia . org/wiki/Matrix _ factorization _(recommender _ systems)
- [2]https://www . emathhelp . net/en/calculators/linear-algebra/SVD-calculator/
- https://en.wikipedia.org/wiki/Singular_value_decomposition
- https://en.wikipedia.org/wiki/Eigenvalues_and_eigenvectors
如果你喜欢读这篇文章,这里有一个与数据科学和机器学习相关的列表,你可能会感兴趣。