TowardsDataScience-博客中文翻译-2020-一百一十-
TowardsDataScience 博客中文翻译 2020(一百一十)
用 StaticFrame 代替熊猫的十大理由
在处理数据帧时,创建更易维护、更不易出错的 Python
作者图片
如果你用 Python 处理数据,你可能会用熊猫。Pandas 提供了近乎即时的满足:复杂的数据处理例程可以用几行代码实现。然而,如果你在大型项目中使用熊猫多年,你可能会遇到一些挑战。复杂的 Pandas 应用程序会产生难以维护且容易出错的 Python 代码。发生这种情况是因为 Pandas 提供了许多方法来做同样的事情,具有不一致的接口,并且广泛支持就地突变。对于那些来自熊猫的人来说,StaticFrame 提供了一个更加一致的界面,减少了出错的机会。这篇文章展示了使用 StaticFrame 代替 Pandas 的十个理由。
为什么是静态框架
在使用 Pandas 开发后端财务系统多年后,我清楚地认识到 Pandas 并不是这项工作的合适工具。Pandas 对标记数据和缺失值的处理,性能接近 NumPy,确实提高了我的生产率。然而,熊猫 API 中的大量不一致导致代码难以维护。此外,熊猫对原位突变的支持导致了严重的出错机会。因此,在 2017 年 5 月,我开始实施一个更适合关键生产系统的库。
现在,经过多年的发展和完善,我们看到了用 StaticFrame 代替 Pandas 在我们的生产系统中取得的优异成绩。用 StaticFrame 编写的库和应用程序更容易维护和测试。我们经常看到 StaticFrame 在大规模、真实世界的用例中表现优于 panda,尽管对于许多独立的操作,StaticFrame 还没有 panda 快。
以下是支持使用 StaticFrame 而不是 Pandas 的十个理由。作为 StaticFrame 的第一作者,我当然对这个演示有偏见。然而,从 2013 年开始与熊猫一起工作,我希望有一些观点可以分享。
所有示例都使用 Pandas 1.0.3 和 StaticFrame 0.6.20。导入使用以下惯例:
>>> import pandas as pd
>>> import static_frame as sf
№ 1:一致且可发现的界面
应用程序编程接口(API)可以在函数的位置、函数的命名方式以及这些函数接受的参数的名称和类型方面保持一致。StaticFrame 偏离了 Pandas 的 API,以在所有这些领域支持更大的一致性。
要创建一个sf.Series
或sf.Frame
,你需要构造函数。Pandas 将其pd.DataFrame
构造函数放在两个地方:根名称空间(pd
,通常是导入的)和pd.DataFrame
类。
例如,JSON 数据是从pd
名称空间上的函数加载的,而记录数据(Python 序列的一个 iterable)是从pd.DataFrame
类加载的。
>>> pd.read_json('[{"name":"muon", "mass":0.106},{"name":"tau", "mass":1.777}]')
name mass
0 muon 0.106
1 tau 1.777>>> pd.DataFrame.from_records([{"name":"muon", "mass":0.106}, {"name":"tau", "mass":1.777}])
name mass
0 muon 0.106
1 tau 1.777
尽管 Pandas 有专门的构造函数,默认的pd.DataFrame
构造函数接受多种多样的输入,包括许多与pd.DataFrame.from_records()
相同的输入。
>>> pd.DataFrame([{"name":"muon", "mass":0.106}, {"name":"tau", "mass":1.777}])
name mass
0 muon 0.106
1 tau 1.777
对于用户来说,这种多样性和冗余性没有什么好处。StaticFrame 将所有的构造函数放在它们构造的类上,并尽可能地集中它们的功能。因为显式的、专用的构造函数更容易维护,所以它们在 StaticFrame 中很常见。比如sf.Frame.from_json()
和sf.Frame.from_dict_records()
:
>>> sf.Frame.from_json('[{"name":"muon", "mass":0.106}, {"name":"tau", "mass":1.777}]')
<Frame>
<Index> name mass <<U4>
<Index>
0 muon 0.106
1 tau 1.777
<int64> <<U4> <float64>>>> sf.Frame.from_dict_records([{"name":"muon", "mass":0.106}, {"name":"tau", "mass":1.777}])
<Frame>
<Index> name mass <<U4>
<Index>
0 muon 0.106
1 tau 1.777
<int64> <<U4> <float64>
显式导致大量的构造函数。为了帮助您找到您正在寻找的东西,StaticFrame 容器公开了一个interface
属性,该属性将调用类或实例的整个公共接口作为一个sf.Frame
提供。我们可以通过使用一个sf.Frame.loc[]
选择来过滤这个表,只显示构造函数。
>>> sf.Frame.interface.loc[sf.Frame.interface['group'] == 'Constructor', 'doc']
<Series: doc>
<Index: signature>
__init__(data, *, index, columns,... Initializer. Args...
from_arrow(value, *, index_depth,... Realize a Frame f...
from_clipboard(*, delimiter, inde... Create a Frame fr...
from_concat(frames, *, axis, unio... Concatenate multi...
from_concat_items(items, *, axis,... Produce a Frame w...
from_csv(fp, *, index_depth, inde... Specialized versi...
from_delimited(fp, *, delimiter, ... Create a Frame fr...
from_dict(mapping, *, index, fill... Create a Frame fr...
from_dict_records(records, *, ind... Frame constructor...
from_dict_records_items(items, *,... Frame constructor...
from_element(element, *, index, c... Create a Frame fr...
from_element_iloc_items(items, *,... Given an iterable...
from_element_loc_items(items, *, ... This function is ...
from_elements(elements, *, index,... Create a Frame fr...
from_hdf5(fp, *, label, index_dep... Load Frame from t...
from_items(pairs, *, index, fill_... Frame constructor...
from_json(json_data, *, dtypes, n... Frame constructor...
from_json_url(url, *, dtypes, nam... Frame constructor...
from_overlay(containers, *, union...
from_pandas(value, *, index_const... Given a Pandas Da...
from_parquet(fp, *, index_depth, ... Realize a Frame f...
from_records(records, *, index, c... Construct a Frame...
from_records_items(items, *, colu... Frame constructor...
from_series(series, *, name, colu... Frame constructor...
from_sql(query, *, connection, in... Frame constructor...
from_sqlite(fp, *, label, index_d... Load Frame from t...
from_structured_array(array, *, i... Convert a NumPy s...
from_tsv(fp, *, index_depth, inde... Specialized versi...
from_xlsx(fp, *, label, index_dep... Load Frame from t...
<<U94> <<U83>
№ 2:一致且丰富多彩的显示屏
熊猫以不同的方式展示它的容器。例如,pd.Series
显示了它的名称和类型,而pd.DataFrame
没有显示这两个属性。如果你显示一个pd.Index
或pd.MultiIndex
,你会得到第三种方法:一个适合eval()
的字符串,当它很大时是不可理解的。
>>> df = pd.DataFrame.from_records([{'symbol':'c', 'mass':1.3}, {'symbol':'s', 'mass':0.1}], index=('charm', 'strange'))>>> df
symbol mass
charm c 1.3
strange s 0.1>>> df['mass']
charm 1.3
strange 0.1
Name: mass, dtype: float64>>> df.index
Index(['charm', 'strange'], dtype='object')
StaticFrame 为所有容器提供了一致的、可配置的显示。sf.Series
、sf.Frame
、sf.Index
和sf.IndexHierarchy
的显示都共享一个公共的实现和设计。这种设计的一个优先考虑的问题是总是显式的容器类和底层数组类型。
>>> f = sf.Frame.from_dict_records_items((('charm', {'symbol':'c', 'mass':1.3}), ('strange', {'symbol':'s', 'mass':0.1})))>>> f
<Frame>
<Index> symbol mass <<U6>
<Index>
charm c 1.3
strange s 0.1
<<U7> <<U1> <float64>>>> f['mass']
<Series: mass>
<Index>
charm 1.3
strange 0.1
<<U7> <float64>>>> f.columns
<Index>
symbol
mass
<<U6>
由于大量的时间花费在可视化地探索这些容器的内容上,StaticFrame 提供了许多显示配置选项,所有这些都通过sf.DisplayConfig
类公开。对于持久的变更,sf.DisplayConfig
实例可以传递给sf.DisplayActive.set()
;对于一次性的更改,sf.DisplayConfig
实例可以传递给容器的display()
方法。
虽然pd.set_option()
可以类似地用于设置熊猫显示特征,但 StaticFrame 提供了更广泛的选项来使类型可被发现。如这个终端动画所示,特定类型可以被着色或者类型注释可以被完全移除。
№ 3:不可变数据:无需防御性副本的高效内存管理
Pandas 在数据输入和从容器中公开的数据的所有权方面表现出不一致的行为。在某些情况下,有可能在熊猫的“背后”变异 NumPy 阵列,暴露出不良副作用和编码错误的机会。
例如,如果我们向一个pd.DataFrame
提供一个 2D 数组,数组的原始引用可以用来“远程”改变pd.DataFrame
中的值。在这种情况下,pd.DataFrame
不保护对其数据的访问,只作为一个共享的可变数组的包装器。
>>> a1 = np.array([[0.106, -1], [1.777, -1]])>>> df = pd.DataFrame(a1, index=('muon', 'tau'), columns=('mass', 'charge'))>>> df
mass charge
muon 0.106 -1.0
tau 1.777 -1.0>>> a1[0, 0] = np.nan *# Mutating the original array.*>>> df *# Mutation reflected in the DataFrame created from that array.*
mass charge
muon NaN -1.0
tau 1.777 -1.0
类似地,有时从pd.Series
或pd.DataFrame
的values
属性中暴露出来的 NumPy 数组可能会发生变异,从而改变pd.DataFrame
中的值。
>>> a2 = df['charge'].values>>> a2
array([-1., -1.])>>> a2[1] = np.nan *# Mutating the array from .values.*>>> df *# Mutation is reflected in the DataFrame.*
mass charge
muon NaN -1.0
tau 1.777 NaN
有了 StaticFrame,就没有了“幕后”变异的漏洞:因为 StaticFrame 管理不可变的 NumPy 数组,所以引用只保存到不可变的数组。如果在初始化时给定了一个可变数组,将会产生一个不可变的副本。不可变数组不能从容器或对底层数组的直接访问中变异。
>>> a1 = np.array([[0.106, -1], [1.777, -1]])>>> f = sf.Frame(a1, index=('muon', 'tau'), columns=('mass', 'charge'))>>> a1[0, 0] = np.nan *# Mutating the original array has no affect on the Frame*>>> f
<Frame>
<Index> mass charge <<U6>
<Index>
muon 0.106 -1.0
tau 1.777 -1.0
<<U4> <float64> <float64>>>> f['charge'].values[1] = np.nan *# An immutable array cannot be mutated*
Traceback (most recent call last):
File "<console>", line 1, in <module>
ValueError: assignment destination is read-only
虽然不可变数据减少了出错的机会,但它也提供了性能优势。例如,当用sf.Frame.relabel()
替换列标签时,底层数据不会被复制。相反,对相同不可变数组的引用在新旧容器之间共享。这样的“无拷贝”操作因此是快速和轻量级的。这与在 Pandas 中做同样的事情时发生的情况形成了对比:相应的 Pandas 方法df.DataFrame.rename()
被强制对所有底层数据进行防御性复制。
>>> f.relabel(columns=lambda x: x.upper()) *# Underlying arrays are not copied*
<Frame>
<Index> MASS CHARGE <<U6>
<Index>
muon 0.106 -1.0
tau 1.777 -1.0
<<U4> <float64> <float64>
№ 4:赋值是一个函数
虽然 Pandas 允许就地赋值,但有时这种操作不能提供适当的派生类型,从而导致不良行为。例如,一个赋给整数pd.Series
的浮点数将在没有警告或错误的情况下截断它的浮点部分。
>>> s = pd.Series((-1, -1), index=('tau', 'down'))
>>> s
tau -1
down -1
dtype: int64>>> s['down'] = -0.333 *# Assigning a float.*>>> s *# The -0.333 value was truncated to 0*
tau -1
down 0
dtype: int64
使用 StaticFrame 的不可变数据模型,赋值是一个返回新容器的函数。这允许评估类型以确保结果数组可以完全包含赋值。
>>> s = sf.Series((-1, -1), index=('tau', 'down'))
>>> s
<Series>
<Index>
tau -1
down -1
<<U4> <int64>>>> s.assign['down'](-0.333) *# The float is assigned without truncation*
<Series>
<Index>
tau -1.0
down -0.333
<<U4> <float64>
StaticFrame 使用一个特殊的assign
接口来执行赋值函数调用。在一个sf.Frame
上,这个接口公开了一个sf.Frame.assign.loc[]
接口,可以用来选择赋值的目标。选择之后,要分配的值通过函数调用传递。
>>> f = sf.Frame.from_dict_records_items((('charm', {'charge':0.666, 'mass':1.3}), ('strange', {'charge':-0.333, 'mass':0.1})))>>> f
<Frame>
<Index> charge mass <<U6>
<Index>
charm 0.666 1.3
strange -0.333 0.1
<<U7> <float64> <float64>>>> f.assign.loc['charm', 'charge'](Fraction(2, 3)) *# Assigning to a loc-style selection*
<Frame>
<Index> charge mass <<U6>
<Index>
charm 2/3 1.3
strange -0.333 0.1
<<U7> <object> <float64>
№ 5:迭代器用于迭代和函数应用
Pandas 具有独立的迭代和函数应用功能。对于pd.DataFrame
上的迭代,有pd.DataFrame.iteritems()
、pd.DataFrame.iterrows()
、pd.DataFrame.itertuples()
和pd.DataFrame.groupby()
;对于pd.DataFrame
上的功能应用,有pd.DataFrame.apply()
和pd.DataFrame.applymap()
。
但是由于函数应用需要迭代,因此将函数应用建立在迭代之上是明智的。StaticFrame 通过提供一系列迭代器(如Frame.iter_array()
或Frame.iter_group_items()
)来组织迭代和函数应用,通过对apply()
的链式调用,这些迭代器也可用于函数应用。迭代器上也有应用映射类型的函数(比如map_any()
和map_fill()
)。这意味着一旦你知道你想如何迭代,函数应用只是一个方法。
例如,我们可以用sf.Frame.from_records()
创建一个sf.Frame
:
>>> f = sf.Frame.from_records(((0.106, -1.0, 'lepton'), (1.777, -1.0, 'lepton'), (1.3, 0.666, 'quark'), (0.1, -0.333, 'quark')), columns=('mass', 'charge', 'type'), index=('muon', 'tau', 'charm', 'strange'))>>> f
<Frame>
<Index> mass charge type <<U6>
<Index>
muon 0.106 -1.0 lepton
tau 1.777 -1.0 lepton
charm 1.3 0.666 quark
strange 0.1 -0.333 quark
我们可以用sf.Series.iter_element()
遍历一列值。我们可以通过使用在从sf.Series.iter_element()
返回的对象上找到的apply()
方法,使用同一个迭代器做函数应用。在sf.Series
和sf.Frame
上都可以找到相同的界面。
>>> tuple(f['type'].iter_element())
('lepton', 'lepton', 'quark', 'quark')>>> f['type'].iter_element().apply(lambda e: e.upper())
<Series>
<Index>
muon LEPTON
tau LEPTON
charm QUARK
strange QUARK
<<U7> <<U6>>>> f[['mass', 'charge']].iter_element().apply(lambda e: format(e, '.2e'))
<Frame>
<Index> mass charge <<U6>
<Index>
muon 1.06e-01 -1.00e+00
tau 1.78e+00 -1.00e+00
charm 1.30e+00 6.66e-01
strange 1.00e-01 -3.33e-01
<<U7> <object> <object>
对于sf.Frame
上的行或列迭代,一系列方法允许指定用于迭代的行或列的容器类型,即,用数组、用NamedTuple
或用sf.Series
(分别为iter_array()
、iter_tuple()
、iter_series()
)。这些方法采用一个轴参数来确定迭代是按行还是按列,并且类似地为函数应用程序公开一个apply()
方法。要对列应用函数,我们可以执行以下操作。
>>> f[['mass', 'charge']].iter_array(axis=0).apply(np.sum)
<Series>
<Index>
mass 3.283
charge -1.667
<<U6> <float64>
将函数应用于行而不是列只需要更改轴参数。
>>> f.iter_series(axis=1).apply(lambda s: s['mass'] > 1 and s['type'] == 'quark')
<Series>
<Index>
muon False
tau False
charm True
strange False
<<U7> <bool>
Group-by 操作只是另一种形式的迭代,具有相同的迭代和函数应用接口。
>>> f.iter_group('type').apply(lambda f: f['mass'].mean())
<Series>
<Index>
lepton 0.9415
quark 0.7000000000000001
<<U6> <float64>
№ 6:严格的仅增长框架
pd.DataFrame
的一个有效用途是加载初始数据,然后通过添加额外的列来生成派生数据。这种方法利用了类型和基础数组的列组织:添加新列不需要重新分配旧列。
StaticFrame 通过提供一个称为sf.FrameGO
的sf.Frame
的严格的、只增长的版本,使得这种方法不容易出错。例如,一旦创建了sf.FrameGO
,就可以添加新的列,而现有的列不能被覆盖或就地改变。
>>> f = sf.FrameGO.from_records(((0.106, -1.0, 'lepton'), (1.777, -1.0, 'lepton'), (1.3, 0.666, 'quark'), (0.1, -0.333, 'quark')), columns=('mass', 'charge', 'type'), index=('muon', 'tau', 'charm', 'strange'))>>> f['positive'] = f['charge'] > 0>>> f
<FrameGO>
<IndexGO> mass charge type positive <<U8>
<Index>
muon 0.106 -1.0 lepton False
tau 1.777 -1.0 lepton False
charm 1.3 0.666 quark True
strange 0.1 -0.333 quark False
这种有限形式的突变满足了实际需要。此外,从一个sf.Frame
到一个sf.FrameGO
的来回转换(使用Frame.to_frame_go()
和FrameGO.to_frame()
)是一个非复制操作:底层的不可变数组可以在两个容器之间共享。
№ 7:日期不是纳秒
Pandas 将所有日期或时间戳值建模为 NumPy datetime64[ns]
(纳秒)数组,而不管纳秒级的分辨率是否实用或合适。这给熊猫制造了一个“Y2262 问题”:超过 2262-04-11 的日期不能被表达。虽然我可以创建一个最长为 2262–04–11 的pd.DatetimeIndex
,但再过一天,Pandas 就会引发一个错误。
>>> pd.date_range('1980', '2262-04-11')
DatetimeIndex(['1980-01-01', '1980-01-02', '1980-01-03', '1980-01-04',
'1980-01-05', '1980-01-06', '1980-01-07', '1980-01-08',
'1980-01-09', '1980-01-10',
...
'2262-04-02', '2262-04-03', '2262-04-04', '2262-04-05',
'2262-04-06', '2262-04-07', '2262-04-08', '2262-04-09',
'2262-04-10', '2262-04-11'],
dtype='datetime64[ns]', length=103100, freq='D')>>> pd.date_range('1980', '2262-04-12')
Traceback (most recent call last):
pandas._libs.tslibs.np_datetime.OutOfBoundsDatetime: Out of bounds nanosecond timestamp: 2262-04-12 00:00:00
由于索引通常用于粒度远小于纳秒的日期时间值(如日期、月份或年份),StaticFrame 提供了所有 NumPy 类型的datetime64
索引。这允许精确的日期-时间类型规范,并避免了基于纳秒的单位的限制。
虽然用 Pandas 不可能,但用 StaticFrame 创建一个扩展到 3000 年的年份或日期的索引是很简单的。
>>> sf.IndexYear.from_year_range(1980, 3000).tail()
<IndexYear>
2996
2997
2998
2999
3000
<datetime64[Y]>>>> sf.IndexDate.from_year_range(1980, 3000).tail()
<IndexDate>
3000-12-27
3000-12-28
3000-12-29
3000-12-30
3000-12-31
<datetime64[D]>
№ 8:层次索引的一致接口
分级索引允许将多个维度放入一个维度中。使用分级索引, n 维数据可以被编码到单个sf.Series
或sf.Frame
中。
分级索引的一个关键特征是在任意深度的部分选择,由此选择可以由每个深度级别的选择的交集组成。熊猫提供了许多方式来表达那些内在的深度选择。
一种方法是超载pd.DataFrame.loc[]
。当使用 Pandas 的层次索引(pd.MultiIndex
)时,pd.DataFrame.loc[]
选择中的位置参数的含义变成了动态的。正是这一点使得 Pandas 代码很难使用层次索引来维护。我们可以通过创建一个pd.DataFrame
并设置一个pd.MultiIndex
来看到这一点。
>>> df = pd.DataFrame.from_records([('muon', 0.106, -1.0, 'lepton'), ('tau', 1.777, -1.0, 'lepton'), ('charm', 1.3, 0.666, 'quark'), ('strange', 0.1, -0.333, 'quark')], columns=('name', 'mass', 'charge', 'type'))>>> df.set_index(['type', 'name'], inplace=True)>>> df
mass charge
type name
lepton muon 0.106 -1.000
tau 1.777 -1.000
quark charm 1.300 0.666
strange 0.100 -0.333
类似于 NumPy 中的 2D 数组,当给pd.DataFrame.loc[]
两个参数时,第一个参数是行选择器,第二个参数是列选择器。
>>> df.loc['lepton', 'mass'] *# Selects "lepton" from row, "mass" from columns*
name
muon 0.106
tau 1.777
Name: mass, dtype: float64
然而,与这种期望相反,有时 Pandas 不会将第二个参数用作列选择,而是用作pd.MultiIndex
内部深度的行选择。
>>> df.loc['lepton', 'tau'] *# Selects lepton and tau from rows*
mass 1.777
charge -1.000
Name: (lepton, tau), dtype: float64
为了解决这种不确定性,Pandas 提供了两种选择。如果需要行和列选择,可以通过将分层行选择包装在pd.IndexSlice[]
选择修饰符中来恢复预期的行为。或者,如果不使用pd.IndexSlice[]
需要内部深度选择,可以使用pd.DataFrame.xs()
方法。
>>> df.loc[pd.IndexSlice['lepton', 'tau'], 'charge']
-1.0>>> df.xs(level=1, key='tau')
mass charge
type
lepton 1.777 -1.0
给予pd.DataFrame.loc[]
的位置参数的含义不一致是不必要的,这使得熊猫代码更难维护:使用pd.DataFrame.loc[]
的意图在没有pd.IndexSlice[]
的情况下变得不明确。此外,提供多种方法来解决这个问题也是一个缺点,因为在 Python 中最好有一种显而易见的方法来做事。
StaticFrame 的sf.IndexHierarchy
提供了更加一致的行为。我们将创建一个等价的sf.Frame
并设置一个sf.IndexHierarchy
。
>>> f = sf.Frame.from_records((('muon', 0.106, -1.0, 'lepton'), ('tau', 1.777, -1.0, 'lepton'), ('charm', 1.3, 0.666, 'quark'), ('strange', 0.1, -0.333, 'quark')), columns=('name', 'mass', 'charge', 'type'))>>> f = f.set_index_hierarchy(('type', 'name'), drop=True)>>> f
<Frame>
<Index> mass charge <<U6>
<IndexHierarchy: ('type', 'name')>
lepton muon 0.106 -1.0
lepton tau 1.777 -1.0
quark charm 1.3 0.666
quark strange 0.1 -0.333
<<U6> <<U7> <float64> <float64>
与 Pandas 不同,StaticFrame 在位置参数的含义上是一致的:第一个参数总是行选择器,第二个参数总是列选择器。对于在sf.IndexHierarchy
中的选择,需要sf.HLoc[]
选择修改器来指定层次中任意深度的选择。有一个显而易见的方法来选择内心深处。这种方法使得 StaticFrame 代码更容易理解和维护。
>>> f.loc[sf.HLoc['lepton']]
<Frame>
<Index> mass charge <<U6>
<IndexHierarchy: ('type', 'name')>
lepton muon 0.106 -1.0
lepton tau 1.777 -1.0
<<U6> <<U4> <float64> <float64>>>> f.loc[sf.HLoc[:, ['muon', 'strange']], 'mass']
<Series: mass>
<IndexHierarchy: ('type', 'name')>
lepton muon 0.106
quark strange 0.1
<<U6> <<U7> <float64>
№ 9:索引总是唯一的
很自然地认为pd.DataFrame
上的索引和列标签是惟一标识符:它们的接口表明它们就像 Python 字典,其中的键总是惟一的。然而,熊猫指数并不局限于唯一值。在带有重复项的pd.DataFrame
上创建索引意味着,对于一些单标签选择,将返回一个pd.Series
,但是对于其他单标签选择,将返回一个pd.DataFrame
。
>>> df = pd.DataFrame.from_records([('muon', 0.106, -1.0, 'lepton'), ('tau', 1.777, -1.0, 'lepton'), ('charm', 1.3, 0.666, 'quark'), ('strange', 0.1, -0.333, 'quark')], columns=('name', 'mass', 'charge', 'type'))>>> df.set_index('charge', inplace=True) # Creating an index with duplicated labels>>> df
name mass type
charge
-1.000 muon 0.106 lepton
-1.000 tau 1.777 lepton
0.666 charm 1.300 quark
-0.333 strange 0.100 quark>>> df.loc[-1.0] # Selecting a non-unique label results in a pd.DataFrame
name mass type
charge
-1.0 muon 0.106 lepton
-1.0 tau 1.777 lepton>>> df.loc[0.666] # Selecting a unique label results in a pd.Series
name charm
mass 1.3
type quark
Name: 0.666, dtype: object
Pandas 对非唯一索引的支持使得客户端代码变得更加复杂,因为它必须处理有时返回一个pd.Series
而有时返回一个pd.DataFrame
的选择。此外,索引的唯一性通常是对数据一致性的简单而有效的检查。
一些 Pandas 接口,比如pd.concat()
和pd.DataFrame.set_index()
,提供了一个名为verify_integrity
的可选惟一性检查参数。令人惊讶的是,熊猫默认禁用了verify_integrity
。
>>> df.set_index('type', verify_integrity=True)
Traceback (most recent call last):
ValueError: Index has duplicate keys: Index(['lepton', 'quark'], dtype='object', name='type')
在 StaticFrame 中,索引总是唯一的。试图设置非唯一索引将引发异常。这个约束消除了在索引中错误引入重复的机会。
>>> f = sf.Frame.from_records((('muon', 0.106, -1.0, 'lepton'), ('tau', 1.777, -1.0, 'lepton'), ('charm', 1.3, 0.666, 'quark'), ('strange', 0.1, -0.333, 'quark')), columns=('name', 'mass', 'charge', 'type'))>>> f
<Frame>
<Index> name mass charge type <<U6>
<Index>
0 muon 0.106 -1.0 lepton
1 tau 1.777 -1.0 lepton
2 charm 1.3 0.666 quark
3 strange 0.1 -0.333 quark
<int64> <<U7> <float64> <float64> <<U6>>>> f.set_index('type')
Traceback (most recent call last):
static_frame.core.exception.ErrorInitIndex: labels (4) have non-unique values (2)
№ 10:去了又回来看熊猫
StaticFrame 旨在与熊猫并肩工作。通过专门的构造器和导出器,例如Frame.from_pandas()
或Series.to_pandas()
,来回切换是可能的。
>>> df = pd.DataFrame.from_records([('muon', 0.106, -1.0, 'lepton'), ('tau', 1.777, -1.0, 'lepton'), ('charm', 1.3, 0.666, 'quark'), ('strange', 0.1, -0.333, 'quark')], columns=('name', 'mass', 'charge', 'type'))>>> df
name mass charge type
0 muon 0.106 -1.000 lepton
1 tau 1.777 -1.000 lepton
2 charm 1.300 0.666 quark
3 strange 0.100 -0.333 quark>>> sf.Frame.from_pandas(df)
<Frame>
<Index> name mass charge type <object>
<Index>
0 muon 0.106 -1.0 lepton
1 tau 1.777 -1.0 lepton
2 charm 1.3 0.666 quark
3 strange 0.1 -0.333 quark
<int64> <object> <float64> <float64> <object>
结论
“数据框”对象的概念在 2009 年 Pandas 0.1 发布之前很久就出现了:数据框的第一个实现可能早在 1991 年就出现在 r 的前身 S 语言中。今天,数据框在各种语言和实现中都有实现。熊猫将继续为广大用户提供优秀的资源。然而,对于正确性和代码可维护性至关重要的情况,StaticFrame 提供了一种替代方案,旨在更加一致并减少出错的机会。
有关 StaticFrame 的更多信息,请参见文档或项目现场。
数据科学面试中你应该知道的十个 SQL 概念
学习聪明,不努力。
由宏向量创建的设计向量—www.freepik.com
SQL 非常强大,有很多功能。然而,当谈到数据科学面试时,大多数公司测试的核心概念真的很少。这 10 个概念出现的频率最高,因为它们在现实生活中应用最多。
在这篇文章中,我将回顾我认为的 10 个最重要的 SQL 概念,这些概念是你在准备面试时应该花大部分时间关注的。
说到这里,我们开始吧!
1。案例当
您很可能会看到许多问题需要使用 CASE WHEN 语句,这仅仅是因为它是一个如此通用的概念。
如果您想根据其他变量分配某个值或类,它允许您编写复杂的条件语句。
鲜为人知的是,它还允许您透视数据。例如,如果您有一个月列,并且希望为每个月创建一个单独的列,则可以使用 CASE WHEN 语句透视数据。
示例问题:编写一个 SQL 查询来重新格式化该表,以便每个月都有一个收入列。
Initial table:
+------+---------+-------+
| id | revenue | month |
+------+---------+-------+
| 1 | 8000 | Jan |
| 2 | 9000 | Jan |
| 3 | 10000 | Feb |
| 1 | 7000 | Feb |
| 1 | 6000 | Mar |
+------+---------+-------+
Result table:
+------+-------------+-------------+-------------+-----+-----------+
| id | Jan_Revenue | Feb_Revenue | Mar_Revenue | ... | Dec_Revenue |
+------+-------------+-------------+-------------+-----+-----------+
| 1 | 8000 | 7000 | 6000 | ... | null |
| 2 | 9000 | null | null | ... | null |
| 3 | null | 10000 | null | ... | null |
+------+-------------+-------------+-------------+-----+-----------+
更多类似问题, 查看 StrataScratch100 个 SQL 问题。
2.选择不同
选择独特是你应该永远记住的。在聚合函数中使用 SELECT DISTINCT 语句非常常见(这是第三点)。
例如,如果您有一个显示客户订单的表,可能会要求您计算每个客户的平均订单数。在这种情况下,您可能希望计算订单总数,而不是客户总数。它可能看起来像这样:
SELECT
COUNT(order_id) / COUNT(DISTINCT customer_id) as orders_per_cust
FROM
customer_orders
3.聚合函数
关于第二点,你应该对 min、max、sum、count 等聚合函数有很深的理解。这也意味着你应该对 GROUP BY 和 HAVING 子句有很深的理解。我强烈建议您花时间来完成练习题,因为有一些创造性的方法可以使用聚合函数。
示例问题:编写一个 SQL 查询,在一个名为 *Person*
的表中查找所有重复的电子邮件。
+----+---------+
| Id | Email |
+----+---------+
| 1 | a@b.com |
| 2 | c@d.com |
| 3 | a@b.com |
+----+---------+**ANSWER:**
SELECT
Email
FROM
Person
GROUP BY
Email
HAVING
count(Email) > 1
4.左连接与内连接
对于那些对 SQL 比较陌生或者已经有一段时间没有使用它的人来说,很容易混淆左连接和内连接。请确保您清楚地了解每个连接如何产生不同的结果。在许多面试问题中,你会被要求做一些连接,在某些情况下,选择一个对另一个是正确和错误答案之间的区别。
5.自连接
现在我们开始更有趣的东西了!SQL 自联接将表与其自身联接起来。你可能认为这没有用,但是你会惊讶于这是多么的普遍。在许多实际设置中,数据存储在一个大表中,而不是许多较小的表中。在这种情况下,可能需要自联接来解决独特的问题。
让我们看一个例子。
示例问题:给定下面的 *Employee*
表,编写一个 SQL 查询,找出收入高于其经理的雇员。在上表中,Joe 是唯一一个收入高于其经理的员工。
+----+-------+--------+-----------+
| Id | Name | Salary | ManagerId |
+----+-------+--------+-----------+
| 1 | Joe | 70000 | 3 |
| 2 | Henry | 80000 | 4 |
| 3 | Sam | 60000 | NULL |
| 4 | Max | 90000 | NULL |
+----+-------+--------+-----------+**Answer:**
SELECT
a.Name as Employee
FROM
Employee as a
JOIN Employee as b on a.ManagerID = b.Id
WHERE a.Salary > b.Salary
6.子查询
子查询也称为内部查询或嵌套查询,是查询中的查询,嵌入在 WHERE 子句中。这是解决需要按顺序进行多次查询才能产生给定结果的独特问题的好方法。查询时,子查询和 WITH AS 语句都非常常用,所以您应该绝对确保知道如何使用它们。
例题:假设一个网站包含两个表,Customers
表和Orders
表。编写一个 SQL 查询来查找从不订购任何东西的所有客户。
Table: Customers.+----+-------+
| Id | Name |
+----+-------+
| 1 | Joe |
| 2 | Henry |
| 3 | Sam |
| 4 | Max |
+----+-------+Table: Orders.
+----+------------+
| Id | CustomerId |
+----+------------+
| 1 | 3 |
| 2 | 1 |
+----+------------+**Answer:**
SELECT
Name as Customers
FROM
Customers
WHERE
Id NOT IN (
SELECT
CustomerId
FROM Orders
)
7.字符串格式
字符串函数非常重要,尤其是在处理不干净的数据时。因此,公司可能会测试你的字符串格式和操作,以确保你知道如何操作数据。
字符串格式包括以下内容:
- 左,右
- 整齐
- 位置
- SUBSTR
- 串联
- 上、下
- 联合
如果你对这些不确定,可以查看一下模式关于清理数据的字符串函数教程。
8.日期时间操作
您肯定会遇到一些涉及日期时间数据的 SQL 问题。例如,您可能需要按月对数据进行分组,或者将变量格式从 DD-MM-YYYY 转换为月份。
您应该知道的一些功能有:
- 提取
- DATEDIFF
例题:给定一个 *Weather*
表,编写一个 SQL 查询,找出所有与前一个(昨天的)日期相比温度更高的日期的 id。
+---------+------------------+------------------+
| Id(INT) | RecordDate(DATE) | Temperature(INT) |
+---------+------------------+------------------+
| 1 | 2015-01-01 | 10 |
| 2 | 2015-01-02 | 25 |
| 3 | 2015-01-03 | 20 |
| 4 | 2015-01-04 | 30 |
+---------+------------------+------------------+**Answer:**
SELECT
a.Id
FROM
Weather a,
Weather b
WHERE
a.Temperature > b.Temperature
AND DATEDIFF(a.RecordDate, b.RecordDate) = 1
9.窗口功能
窗口函数允许您对所有行执行聚合值,而不是只返回一行(这是 GROUP BY 语句所做的)。如果您想对行进行排序、计算累积和等等,这是非常有用的。
例题:写一个查询得到工资最高的 *empno*
。确保您的解决方案能够处理领带!
depname | empno | salary |
-----------+-------+--------+
develop | 11 | 5200 |
develop | 7 | 4200 |
develop | 9 | 4500 |
develop | 8 | 6000 |
develop | 10 | 5200 |
personnel | 5 | 3500 |
personnel | 2 | 3900 |
sales | 3 | 4800 |
sales | 1 | 5000 |
sales | 4 | 4800 |**Answer:**
WITH sal_rank AS
(SELECT
empno,
RANK() OVER(ORDER BY salary DESC) rnk
FROM
salaries)
SELECT
empno
FROM
sal_rank
WHERE
rnk = 1;
10.联盟
作为奖励,#10 是工会!虽然这个问题不经常出现,但偶尔会有人问你这个问题,大体了解一下是有好处的。如果有两个列相同的表,并且想要合并它们,这时就应该使用 UNION。
同样,如果你不是 100%确定它是如何工作的,我会做一些快速的谷歌搜索来了解它。😃
感谢阅读!
仅此而已!我希望这对你的面试准备有所帮助,并祝你在未来的努力中好运。我确信,如果你对这 10 个概念了如指掌,那么在处理大多数 SQL 问题时,你会做得很好。
特伦斯·申
- 如果你喜欢这个, 在 Medium 上关注我 了解更多
- 查看更多 SQL 练习题
编程时节省时间和减少挫折的十个技巧⏳
另外还有一个奖金计划,可以更快地解决错误😀
在我坚持下去之前,我开始和退出编程至少有四次。我对我的数据科学学生的调查显示,这个故事并不罕见。
许多错误的开始部分是由于学习编码的缓慢而乏味的过程以及伴随而来的挫折。因此,我热衷于消除那些阻碍人们成为合格程序员的不必要的障碍。障碍总是会有的,但是没有理由去设置不必要的障碍。😁
这里有 10 个技巧可以帮助你更快地跨越障碍,并获得一种能力感。如果你已经编码多年,这些技巧中的许多对你来说可能是次要的或显而易见的,但是当开始时,没有什么是显而易见的。即使你从手机被称为车载电话的时候就开始编写代码,也可能有一两个小技巧可以节省你的时间。🎉
10 多条提示。来源:https://pixabay.com/
十条建议
- 分割你的屏幕。大多数时候,当你学习编码时,我建议你把屏幕分成两个并排的面板——一个用于你在 Jupyter 笔记本或代码编辑器中编写的代码,另一个用于你的网络浏览器。您将使用 web 浏览器查看文档、堆栈溢出和教程。如果你的屏幕太小,我建议你买一个更大的——如果你负担得起的话。在美国,你可以在亚马逊上花 100 多美元买到一台 25 英寸的显示器。随着你越来越熟练,你会经常想要专注于一件作品。使用键盘快捷键可以在分屏和全屏之间切换。对于 Mac,我喜欢 2.99 美元的 BetterSnapTool ,一些 Windows 选项在本文中讨论。🖥
- 排除杂念。电脑和手机上的静音通知。如果可以的话,把你的手机拿开。如果你在嘈杂的地方,播放一些舒缓的器乐或使用降噪耳机。你越能集中注意力,你就能学得越快。🎻
- 键入示例代码。你不能只看一个教程就希望记住它。复制粘贴价值不大。你得把它打出来。那就延伸一下。然后凭记忆打出来。“从理论上讲,理论和实践之间没有区别。实际上是有的。”——本雅明·布鲁斯特原本——不是约吉·贝拉。⚾️
- 多日学习。学习一个新的代码概念,并在几天内对自己进行测试。研究表明,在长期记忆方面,间隔重复比死记硬背有效得多。🚚
- 寓教于乐。向他人清楚地解释一个概念——大声地或者以书面形式——迫使你理解这个概念的本质,建立关系,并进行类比。你加强了大脑中的联系。这个技巧是优秀的费曼学习技巧的一部分。📚
- 语言前库。在你尝试使用一个编程语言的库之前,要充分理解它。变量类型不是最令人兴奋的话题。具有属性和方法的类可能看起来很高级。但是一旦你真正理解了一门语言,这个库就更容易使用了。你会犯很少的错误,节省很多时间。🎉
- 一次学一件事。不要试图同时学习两件事。你不是学得慢两倍,而是慢 10 倍。😉相关地,不要被闪亮的东西分散注意力。有一个地方来保存看起来像诱人的兔子洞跳下来的网址。把与你现在正在学习的东西不相关的网址放在安全的地方。一个标签是一个方便的 Chrome 扩展,用于保存标签以备后用。
- 睡眠,锻炼,保持活力。😴🏃🏾♀️Put 你的大脑处于一个强大的位置,形成和加强神经连接。🧠在我这本令人难忘的 Python 书籍中学习了更多帮助你保持敏锐的技巧。
- 学会更快地键入代码。 ⌨️你输入不寻常的符号越多,你就越快。然而,一点点的意向性可以显著加快这个过程。投资学习良好的触摸打字形式。这里有一个来自 SpeedCoder 的小练习。
- 成为有键盘快捷键的高手。以下是 Chrome 、 Mac 和 [Windows](https://support.microsoft.com/en-us/help/12445/windows- keyboard-shortcuts) 的快捷方式链接。如果你是数据科学家或数据分析师,我在这里做了一个 GitHub Gist 的 Jupyter 实验室快捷方式。我为 Mac 或 Linux 终端创建了快捷方式,在
~/.bash_profile
:alias gs="git status"
向 Bash 概要文件添加了如下代码行。然后重启你的终端,输入两个键而不是两个单词。😉
额外收获:学会快速解决错误
这是一个如此大的话题,却没有得到足够的关注,以至于我正在考虑就此写一整篇文章。相反,这里有一个额外的部分——一个更快解决错误的 5 步计划。
翻译:当 shape 不存在时,你调用它作为方法,你想得到一个属性。
每个程序员都会看到很多错误。一旦你有了经验,大多数问题只会耸耸肩,因为你可以在一分钟内解决它们。
然而,当你开始时,一个错误可能需要 20 倍的时间来解决。对于一个初学者来说,错误会让你感觉像是在齐踝深的泥泞中跋涉。
模糊的错误。来源:https://pixabay.com/
这里有一个快速解决 95%错误的策略。
所以你有一个错误,你应该怎么做?
- 查找错别字——丢失的括号,或者拼写错误的变量或函数。语法突出显示在这里会有所帮助。一定要在提供代码高亮的软件中打字,以提高避免或快速修复缩进、不对称括号和类似错误的机会。有很多好的代码编辑器可供选择。如果你刚刚开始,我建议vs code——它拥有最大的市场份额,它是免费的,并且有许多方便的功能。
- 首先读取错误消息堆栈跟踪的顶部和底部。中间的代码一般帮助不大。然后,根据错误消息中的线索,查看您的代码,看能否找出问题所在。如果不能快速解码错误消息,将消息的最后一行复制粘贴到 Google(如果使用 Python。其他语言可能会在堆栈跟踪之前显示错误消息)。一个新的程序员最大的错误是没有足够快地向谷歌求助。
3.筛选网上资源去粗取精。🌾这里有一些启发,可以帮助你更快地找到更高质量的资源。😀
栈溢出、中、 Reddit 、黑客新闻、 Dev 等网络社区都有 upvotes 或 claps 等反馈指标,以显示哪些内容是针对目标的。这些度量标准并不完美,但是它们通常工作得很好。栈溢出、数据科学媒体出版物、关于回购的 GitHub 问题以及语言或库的官方文档是我找到最多解决方案的网站。
瞄准目标。来源:https://pixabay.com/
谷歌搜索结果中的博客质量时好时坏。如果你正在使用 Python,我可以根据我的经验推荐一些不错的网站:
4.避免旧的资源。对于大多数问题,您需要的是不超过几年的资源。当在谷歌上搜索错误的解决方案时,过滤掉旧的结果——要么通过对搜索参数进行时间限制,要么在查看结果片段时非正式地过滤。
使用谷歌搜索工具限制搜索结果的时间
如果您刚开始学习 Python,2009 年关于 Python 2 的内容可能对您没有帮助。🐍
API 在不断发展,而事情是如何完成的通常不是今天推荐的方法。你更有可能在相对最近的结果中找到一个合适的答案。📆
如果你第一次尝试不成功,那么下一次浏览旧帖子和低质量网站是值得的。
5.了解错误代码的含义。 这里是Python 的常见错误代码和解释列表。这里有一个非常好的流程图来帮助你处理常见的 Python 错误。
如果使用 Python pandas 库进行数据操作,会出现一些常见错误。下面是我用 Jupyter 笔记本写的 GitHub 要点以及如何解决它们:
常见熊猫错误:https://gist . github . com/disc diver/2 F8 df 1c 3 f1 c 66 f 47129568 a82c 0666 e 5
如果 Python 和 pandas 能提供真正有用的错误消息,包括对错误可能原因的解释,那就太好了。如果有人想解决它,那听起来像是一个有价值的项目!👍
这就是我更快处理错误的计划。如果你有其他建议,请告诉我。😀
包装
希望这 10 个减少挫败感和提高速度的建议能节省你的时间。希望解决错误信息的 5 步计划能帮助你或你认识的人更快地学习。🚀
有了正确的心态,一点指导和大量的实践,任何人都可以学习编码。这可能不会很快或很容易,但希望这篇文章会让它不那么令人沮丧。
如果你觉得这很有帮助,请在你最喜欢的社交媒体上分享,这样其他人也可以找到它。
我写关于 Python、Docker、数据科学等等的文章。如果你对此感兴趣,请在这里阅读更多和关注我。😄
如果你对帮助你提高工作效率的技巧感兴趣,我在我每月的 Data Awesome 时事通讯中分享了很多。
快乐学习!🚀
提高 Python 代码速度的十个技巧
君的轻音乐
每一步的微小进步,整体的巨大飞跃
Python 很慢。
我打赌你可能会多次遇到这种关于使用 Python 的反驳,尤其是来自C
或C++
或Java
世界的人。在许多情况下都是这样,例如,循环或排序 Python 数组、列表或字典有时会很慢。毕竟,开发 Python 是为了让编程变得有趣和简单。因此,Python 代码在简洁性和可读性方面的改进必须以性能为代价。
话虽如此,近年来已经做了许多努力来提高 Python 的性能。我们现在可以通过使用numpy
、scipy
、pandas
和[numba](http://numba.pydata.org/)
以高效的方式处理大型数据集,因为所有这些库都在C/C++
中实现了它们的关键代码路径。还有另一个令人兴奋的项目, Pypy 项目,与 Cpython (最初的 python 实现)相比,它将 Python 代码的速度提高了 4.4 倍。
Pypy 的缺点是它对一些流行的科学模块(如 Matplotlib、Scipy)的覆盖有限或不存在,这意味着您不能在 Pypy 的代码中使用这些模块。
除了这些外部资源,在日常编码实践中,我们还能做些什么来加速 Python 代码呢?今天和大家分享一下我在学习 Python 过程中经常用到的 10 招。
像往常一样,如果你想自己重新运行这篇文章中的代码,可以从 my Github 访问所有需要的数据和笔记本。
1。熟悉内置函数
图 1 |Python 3 中的内置函数
Python 自带了许多在C
中实现的内置函数,这些函数非常快并且维护良好(图 1)。我们至少应该熟悉这些函数名,并且知道在哪里可以找到它们(一些常用的与计算相关的函数有abs()
、len()
、max()
、min()
、set()
、sum()
)。因此,每当我们需要进行一个简单的计算时,我们可以走正确的捷径,而不是笨拙地编写自己的版本。
让我们以内置函数set()
和sum()
为例。如图 2 所示,使用set()
和sum()
分别比我们自己编写的函数快 36.1 倍和 20.9 倍。
图 2 | set()和 sum()函数的示例
2。**sort()**
vs**sorted()**
这两个函数都可以对列表进行排序。
如果我们只想获得一个已排序的列表,而不关心原始列表,那么无论是对于基本排序还是在使用 **key**
参数(key
参数指定了在进行比较之前要在每个列表元素上调用的函数)时,**sort()**
都比 **sorted()**
要快一点,如图 3 所示。
这是因为sort()
方法就地修改列表,而sorted()
构建一个新的排序列表并保持原始列表不变。换句话说,a_long_list
本身的值的顺序实际上已经改变了。
图 3| sort()和 sorted()
然而,sorted()
比sort()
更通用。这是因为sorted()
接受任何 iterable,而sort()
只为列表定义。因此,如果我们想对列表之外的东西进行排序,sorted()
是可以使用的正确函数。例如,我们可以根据字典的keys
或values
对其进行快速排序(图 4)。
图 4 |已排序的()字典
3。用符号代替它们的名字
如图 5 所示,当我们需要一个空的字典或列表对象时,不用使用dict()
或list()
,直接调用{}
(至于空集,需要使用set()
本身)和[]
。这个技巧不一定会加速代码,但是会让代码更复杂。
图 5 |直接使用 list()和 dict()符号
4。列表理解
通常,当我们需要根据某些规则从一个旧列表创建一个新列表时,我们使用一个for
循环来遍历旧列表,并根据规则转换它的值,然后保存在一个新列表中。例如,假设我们想从another_long_list
中找到所有偶数,我们可以使用以下代码:
even_num = []
for number in another_long_list:
if number % 2 == 0:
even_num.append(number)
但是,有一种更简洁、更优雅的方法可以实现这一点。如图 6 所示,我们把原来的 **for**
循环放在仅仅一行代码中。而且,速度提高了差不多 2 倍。
图 6 |列表理解
结合规则 3 ,我们也可以将列表变成字典或集合,只需将[]
改为{}
。让我们重写图 5 中的代码,我们可以省略赋值的步骤,在符号内部完成迭代,就像这个sorted_dict3 = {key: value for key, value in sorted(a_dict.items(), key=lambda item: item[1])}
。
要分解这个,从最后开始。函数“sorted(a_dict.items(), key=lambda item: item[1])
”返回给我们一个元组列表(图 4)。这里,我们使用多重赋值来解包元组,对于列表中的每个元组,我们将key
赋给它的第一项,将value
赋给它的第二项(因为我们知道在这种情况下每个元组中有两项)。最后,每一对key
和value
都保存在一本字典中。
5。使用 **enumerate()**
作为数值和索引
有时,当我们遍历一个列表时,我们希望在表达式中同时使用它的值和索引。如图 7 所示,我们应该使用enumerate()
,它将列表的值转换成索引和值对。这也将我们的代码速度提高了大约 2 倍。
图 7 | enumerate()示例
6。使用 **zip()**
打包和解包多个迭代
在某些情况下,我们需要遍历两个或更多的列表。然后我们可以使用zip()
函数,它将多个列表转换成一个元组列表(图 8)。注意,列表最好长度相同,否则,zip()
会在较短的列表结束后立即停止。
图 8 | zip()示例
相反,要访问列表中每个元组的条目,我们也可以通过添加星号(*)和使用多重赋值来解压缩元组列表,就像这样,letters1, numbers1 = zip(*pairs_list)
。
7。组合 **set()**
和 **in**
当我们想检查一个值是否存在于一个列表中时,一个笨拙的方法是构造这样一个函数:
**# Construct a function for membership test**
def check_membership(n):
for element in another_long_list:
if element == n:
return True
return False
然后调用check_membership(value)
看another_long_list
里面的value
是否。然而,一种 pythonic 式的方法是通过调用value in another_long_list
来使用in
,如图 9 所示。这就像你直接问 python“嘿 Python,你能告诉我value
是否在another_long_list
里面吗”。
图 9 |使用 in 和 set()检查成员资格
为了更有效,我们应该首先使用set()
从列表中删除重复项,然后测试 set 对象中的成员资格。通过这样做,我们减少了需要检查的元素的数量。此外,in
是一个非常快速的集合操作。
从图 9 中可以看出,尽管构建 set 对象花费了 20 毫秒,但这只是一次性投资,检查步骤本身仅用了 5.2 秒,这是 1962 倍的改进。
8。检查变量是否为真
不可避免地,我们会使用大量的if
语句来检查空变量、空列表、空字典等等。我们也可以从这里节省一点时间。
如图 10 所示,我们不需要在if
语句中显式地声明== True
或is True
,取而代之的是我们只使用变量名。这节省了魔法函数[__eq__](https://stackoverflow.com/questions/3588776/how-is-eq-handled-in-python-and-in-what-order)
用于比较两边值的资源。
图 10 |只需检查变量
同样,如果我们需要检查变量是否为空,我们只需要说if not string_returned_from_function:
。
9。使用计数唯一值**Counters()**
假设我们试图计算在规则 1 、a_long_list
中生成的列表中的唯一值。一种方法是创建一个字典,其中的键是数字,值是计数。当我们迭代列表时,如果它已经在字典中,我们可以增加它的计数,如果它不在字典中,我们可以将它添加到字典中。
num_counts = {}
for num in a_long_list:
if num in num_counts:
num_counts[num] += 1
else:
num_counts[num] = 1
然而,更有效的方法是在一行代码num_counts2 = Counter(a_long_list)
中使用 集合 中的Counter()
。是的,就是这么简单。如图 11 所示,它比我们编写的函数大约快 10 倍。
如果我们想知道 10 个最常见的数字,Counter()
实例还有一个非常方便的most_common
方法。
图 11 |计数器()示例
总之,收藏是一个很神奇的模块,我们应该把它保存到我们的日常工具箱中,随时使用。
10。将 **for**
循环放入函数中
可能有一段时间,我们构建了一个函数,并且需要重复这个函数给定的次数。一个显而易见的方法是我们构建一个函数,然后将这个函数放入一个for
循环中。
但是,如图 12 所示,我们没有重复执行函数 100 万次(a_long_list 的长度为 1,000,000),而是在函数内部集成了for
循环。这为我们节省了大约 22%的运行时间。这是因为函数调用是昂贵的,通过将函数写入列表理解来避免它是更好的选择。
图 12 |函数内部的 put for 循环
仅此而已!感谢你阅读这篇文章。希望有些小技巧能对你有用。此外,您还使用了哪些其他方法来加速 Python 代码?如果你能留下评论来分享它们,我将不胜感激。
以下是您可能感兴趣的链接:
- 如何使用
[sort()](https://docs.python.org/3/howto/sorting.html)
和[sorted()](https://docs.python.org/3/howto/sorting.html)
对列表进行排序 - 在 Python 中何时使用列表理解
- 将代码转换成漂亮、地道的 Python 语言
- Python 编程语言的优缺点
和往常一样,我欢迎反馈、建设性的批评以及听到关于您的数据科学项目的信息。可以通过 Linkedin 和我的网站找到我。
在 R 中执行常见数据任务的十种最新方法
用可爱企鹅的数据集来了解这十个简单的例子
无论您是否是 tidyverse 的粉丝,毫无疑问,这个 R 包集合提供了一些整洁而有吸引力的方式来处理数据,这些方式对用户来说通常非常直观。在 tidyverse 包的早期版本中,用户控制输出的一些元素被牺牲掉了,以利于新手能够掌握和容易使用的更简单的功能。在最近对dplyr
和tidyr
的更新中,在恢复这种控制方面有了重大进展。
这意味着 tidyverse 中有一些您可能不知道的新函数和方法。它们允许您更好地按照自己的意愿转换数据,并更灵活地执行操作。它们还提供了新的替代方法来执行任务,如嵌套、建模或绘图,使您的代码更具可读性和可理解性。事实上,我确信用户仅仅是对这个重要的软件包的最新更新的皮毛。
任何程序员都有责任跟上方法的发展。下面是最新的 tidyverse 更新提供的处理常见数据任务的新方法的十个示例。对于这些例子,我将使用新的帕尔默企鹅数据集,这是对有争议的虹膜数据集的替代,众所周知,Fischer 在他围绕优生学的工作中使用了该数据集。正如我们将看到的,它实际上是一个更好的教学和演示数据争论的全面数据集,我鼓励你使用和探索它。
首先,让我们加载 tidyverse 包和帕尔默企鹅数据集,并快速浏览一下。我鼓励您在尝试复制本文中的工作之前安装这些包的最新版本。
我们可以看到,该数据集提供了不同物种、性别和原产地的企鹅的各种解剖特征的几种测量方法,以及采取这些测量方法的年份。
1.选择数据中的列
tidyselect
现在内置了助手函数,允许您根据常见条件使用dplyr::select()
选择列,从而节省时间。在这种情况下,如果我想将数据集简化为仅包含账单测量值,我可以使用这个(注意,所有测量值列都包含一个下划线):
一整套tidyselect
助手函数可以在文档这里找到。
2.重新排序数据中的列
dplyr::relocate()
允许以一种新的方式对特定的列或列集进行重新排序。例如,如果我想确保我的所有测量列都在数据集的末尾,我可以使用这个(注意我的最后一列是year
):
类似于.after
你也可以在这里用.before
作为论元。
3.控制突变的列位置
你会注意到在penguins
数据集中,每只企鹅都没有唯一的标识符。当数据集中有多个相同物种、岛屿、性别和年份的企鹅时,这可能会有问题。为了解决这个问题并为后面的例子做准备,让我们使用dplyr::mutate()
添加一个惟一的标识符,这里我们可以说明mutate()
现在如何允许您以类似于relocate()
的方式定位新列:
4.从宽到长的转变
penguins
数据集显然是一种广泛的形式——它给出了跨列的多个观察值。出于多种原因,我们可能希望将数据从宽型转换为长型。在长数据中,每个观察值都有自己的行。在tidyr
中旧的函数gather()
在这类任务中很流行,但是它的新版本pivot_longer()
更加强大。在这种情况下,我们在这些列名中有不同的正文部分、度量和单位,但是我们可以像这样非常简单地将它们分开:
5.由长变宽
从长移回宽也一样容易。pivot_wider()
比旧的spread()
更加灵活:
6.跨多个列运行组统计信息
dplyr
如何使用across
副词将多个汇总函数应用于分组数据,帮助您提高效率。如果我们想总结所有企鹅的喙和鳍状肢的尺寸,我们会这样做:
7.控制跨多列汇总时输出列的命名方式
您将在上面看到penguin_stats
中的多个列是如何被赋予默认名称的,这并不直观。如果您命名了您的汇总函数,那么您可以使用.names
参数来精确地控制您想要如何命名这些列。这使用了glue
符号。例如,这里我想构造新的列名,方法是获取现有的列名,删除任何下划线或“mm”度量,并使用下划线粘贴到汇总函数名:
8.跨数据子集运行模型
summarise()
的输出现在可以是任何东西,因为dplyr
现在允许不同的列类型。您可以生成汇总向量、数据帧或其他对象,如模型或图形。
如果你想为每个物种运行一个模型,你可以这样做:
将模型对象保存在数据帧中通常不是很有用,但是您可以使用其他面向整洁的包来总结模型的统计数据,并将它们作为完美集成的数据帧返回:
9.嵌套数据
我们经常需要处理数据的子集,按子集对数据进行分组会很有用,这样我们就可以在所有数据子集上应用通用的函数或操作。例如,也许我们想看看我们不同种类的企鹅,并为它们制作一些不同的图表。以前,基于子集的分组可以通过以下有些笨拙的 tidyverse 函数组合来实现。
新功能nest_by()
提供了一种更直观、更快捷的方式来做同样的事情:
请注意,嵌套数据将存储在名为data
的列中,除非您使用.key
参数指定其他方式。
10.跨子集绘图
有了nest_by()
和我们现在可以总结或变异几乎任何类型的对象的事实,这允许我们跨子集生成图形,并将它们存储在数据帧中以备后用。让我们为我们的三个企鹅种类散点图比尔长度和深度:
现在我们可以很容易地显示不同的散点图,例如,我们的企鹅体现了辛普森悖论:
最初我是一名纯粹的数学家,后来我成为了一名心理计量学家和数据科学家。我热衷于将所有这些学科的严谨性应用到复杂的人的问题上。我也是一个编码极客和日本 RPG 的超级粉丝。在LinkedIn或Twitter上找我。也可以看看我在【drkeithmcnulty.com】上的博客。
帕尔默企鹅作品由 @allison_horst (经许可使用)
tensor board——tensor flow 模型的可视化套件
学习使用 TensorBoard 可视化指标、图表、权重直方图和 Tensorflow 模型的偏差
在本文中,您将学习使用 TensorBoard 显示在 TensorFlow 中创建的深度学习模型在不同时期的指标、图表、图像以及权重和偏差直方图,以及您可能会遇到的常见问题。
什么是张量板?
TensorBoard 是 Tensorflow 的一个可视化工具包,用于显示不同的指标、参数和其他可视化内容,帮助调试、跟踪、微调、优化和共享您的深度学习实验结果。
通过 TensorBoard 可以追踪到哪些不同的东西?
TensorBoard 允许您跟踪
- 度量 : 损失和准确度随着每个时期而变化。跟踪训练周期中的损失和准确性将有助于您了解模型是否过度拟合。
- 模型图:可视化深度学习模型,了解其构建是否正确
- 分布直方图:可视化各个时期的权重和偏差直方图
- 图像:在 TensorBoard 中为你的数据集可视化任意图像
为什么要用 TensorBoard?
你正在开发一个图像分类深度神经网络,希望拥有 98%以上的准确率。
您希望跟踪模型在每个时期的准确性和损失;另外您可能希望使用不同的超参数值(如学习率、优化器、退出等)来跟踪和分析您的模型的准确性和损失。超参数不同值的跟踪精度将帮助您微调模型。
深度学习模型在计算上非常昂贵,使用不同的超参数值可视化我们的实验结果,并跟踪几个时期的准确性和损失,将有助于快速微调模型。
您可以基于不同的数据流图跟踪模型的准确性和损失,这些数据流图具有不同的隐藏层数和隐藏层中的单元数。
TensorBoard 通过跟踪精度和损失,帮助可视化模型中的张量流,以便进行调试和优化。
TensorBoard 将计算图形、训练参数、度量和超参数可视化,这将有助于跟踪模型的实验结果,从而更快地对模型进行微调。
有了 TensorBoard.dev ,你就可以托管你的深度学习模型实验结果,分享给你的团队。任何人都可以通过链接查看数据,所以不要上传敏感数据。
如何使用 TensorBoard?
我们将使用 TensorBoard 使用猫狗数据集可视化标量、图形和分布。
导入所需的库
**import tensorflow as tf
import datetime
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Conv2D, Flatten, Dropout, MaxPooling2D
from tensorflow.keras.preprocessing.image import ImageDataGenerator, img_to_array, load_img**
我用过 TensorFlow 2.0.0 版本。
加载 TensorBoard 笔记本扩展
# Load the TensorBoard notebook extension
**%load_ext tensorboard**
创建图像分类深度学习模型
设置培训的关键参数
**BASE_PATH = 'Data\\dogs-vs-cats\\train\\'
TRAIN_PATH='Data\\dogs-vs-cats\\train_data\\'
VAL_PATH='Data\\dogs-vs-cats\\validation_data\\'batch_size = 32
epochs = 60
IMG_HEIGHT = 150
IMG_WIDTH = 150**
重新缩放并对训练图像应用不同的增强
**train_image_generator = ImageDataGenerator( rescale=1./255, rotation_range=45, width_shift_range=.15, height_shift_range=.15, horizontal_flip=True, zoom_range=0.3)**
重新调整验证数据
**validation_image_generator = ImageDataGenerator(rescale=1./255)**
为训练和验证数据集生成批量归一化数据
**train_data_gen = train_image_generator.flow_from_directory(batch_size = batch_size, directory=TRAIN_PATH, shuffle=True, target_size=(IMG_HEIGHT, IMG_WIDTH), class_mode='categorical')val_data_gen = validation_image_generator.flow_from_directory(batch_size = batch_size, directory=VAL_PATH, target_size=(IMG_HEIGHT, IMG_WIDTH), class_mode='categorical')**
创建并编译模型
**def create_model():
model = Sequential([
Conv2D(16, 3, padding='same', activation='relu',
input_shape=(IMG_HEIGHT, IMG_WIDTH ,3)),
MaxPooling2D(),
Dropout(0.2),
Conv2D(32, 3, padding='same', activation='relu'),
MaxPooling2D(),
Conv2D(64, 3, padding='same', activation='relu'),
MaxPooling2D(),
Conv2D(128, 3, padding='same', activation='relu'),
MaxPooling2D(),
Dropout(0.2),
Flatten(),
Dense(512, activation='relu'),
Dense(2, activation='softmax')])
return model**# Create and Compile the model
**model= create_model()
model.compile(optimizer='adam', loss='categorical_crossentropy',
metrics=['accuracy'])**
使用张量板拟合模型
为了可视化损失和准确性、模型图以及权重和偏差的直方图,您需要创建日志并存储详细信息。TensorBoard 将使用这些日志文件来显示详细信息。
要创建日志文件,在拟合模型时使用TF . keras . callbacks . tensor board。
**log_dir=r'\Tensorboard_ex\logs_1\fit\' + datetime.datetime.now().strftime('%Y%m%d-%H%M%S')
tensorboard_callback= tf.keras.callbacks.TensorBoard(log_dir=log_dir,
histogram_freq=1)**
为了对每个层和每个时期的权重和偏差进行直方图计算,我们将 histogram_freq=1 设置为默认关闭。
拟合模型
**model.fit_generator(
train_data_gen,
steps_per_epoch=1000,
epochs=epochs,
validation_data=val_data_gen,
validation_steps=1000,
callbacks=[tensorboard_callback]
)**
可视化张量板仪表盘
您可以使用 Jupyter 笔记本中的不同命令查看 TensorBoard 仪表板
%tensorboard --logdir="\TEnsorBoard_ex\logs_1\fit"
或者
**%tensorboard — logdir log_dir**
或者
通过指定存储事件日志的目录来启动 TensorBoard。这些事件日志是在我们拟合模型时创建的。
python -m tensorboard.main — logdir=”TensorBoard_ex\logs_1\fit”
在 Chrome 地址栏输入 http://localhost:6006/ ,你现在可以看到 Tensorboard
标量信息,包含 20 个时期内训练和验证数据集的准确性和损失
带有图像分类模型图形的张量板显示模型是否构建正确
你也可以查看非标量张量在多次迭代中的分布
直方图在多次迭代中显示相同的张量非标量张量变量,但显示为三维直方图
TensorBoard 常见问题
当前数据集没有活动的仪表板
杀死所有当前运行的 Tensorboard 任务。在命令提示符下键入以下命令
**taskkill /im tensorboard.exe /f**
**del /q %TMP%\.tensorboard-info\***
现在,通过指定存储事件日志的目录来启动 TensorBoard。这些事件日志是在我们拟合模型时创建的。
python -m tensorboard.main — logdir=”TensorBoard_ex\logs_1\fit”
有时,您可能需要多次尝试这些语句才能查看 Tensorboard 仪表板。
结论:
针对模型的不同元素的 TensorBoard 可视化使得更容易调试和优化深度学习模型实验,以获得更好的准确性。
下一步是什么?
在下一篇文章中,您将学习使用 TensorBoard 使用超参数调整来微调深度学习模型。
参考资料和灵感:
[## TensorBoard | TensorFlow 入门
在机器学习中,要改进某样东西,你通常需要能够测量它。TensorBoard 是一款提供…
www.tensorflow.org](https://www.tensorflow.org/tensorboard/get_started) [## Jupyter 中的 TensorBoard“本地主机拒绝连接”问题(Windows 问题#2481 …
解散 GitHub 是超过 5000 万开发者的家园,他们一起工作来托管和审查代码,管理项目,以及…
github.com](https://github.com/tensorflow/tensorboard/issues/2481) [## [Windows]tensor board-需要从与 logdir 问题#7856 相同的驱动器启动…
解散 GitHub 是超过 5000 万开发者的家园,他们一起工作来托管和审查代码,管理项目,以及…
github.com](https://github.com/tensorflow/tensorflow/issues/7856)
张量板:超参数优化
了解如何使用 TensorBoard 的 HParamas 仪表板为深度学习模型找到最佳超参数。
先决条件:
tensor board——tensor flow 模型的可视化套件
在本文中,您将学习超参数优化,然后使用 TensorBoard 显示超参数优化的结果。
深度神经网络背景下的超参数是什么?
你在深度学习神经网络中的目标是找到节点的权重,这将帮助我们理解图像、任何文本或语音中的数据模式。
为此,您可以使用为模型提供最佳准确度和精度的值来设计神经网络参数。
那么,这些被称为超参数的参数是什么呢?
用于训练神经网络模型的不同参数称为超参数。这些超参数像旋钮一样被调整,以提高神经网络的性能,从而产生优化的模型。
神经网络中的一些超参数是
- 隐藏层数
- 隐藏层中单元或节点的数量
- 学习率
- 辍学率
- 历元或迭代
- 像 SGD,Adam,AdaGrad,Rmsprop 等优化者。
- 激活功能,如 ReLU、sigmoid、leaky ReLU 等。
- 批量大小
如何实现超参数优化?
超参数优化是找到超参数值的过程,如优化器、学习率、辍学率等。深度学习算法的一部分,它将提供最好的模型性能。
您可以使用以下技术执行超参数优化。
- 手动搜索
- 网格搜索 : 对产生笛卡尔积的指定超参数的所有可能组合进行彻底搜索。
- 随机搜索 : 随机选择超参数,并不是每个超参数组合都尝试。随着超参数数量的增加,随机搜索是更好的选择,因为它可以更快地获得超参数的良好组合。
- 贝叶斯优化:In 合并关于超参数的先验数据,包括模型的精度或损失。先验信息有助于确定模型超参数选择的更好近似。
为了可视化 TensorBoard 上模型的超参数调整,我们将使用网格搜索技术,其中我们将使用一些超参数,如节点数量、不同的优化器或学习率以及不同的退出率,并查看模型的准确性和损失。
为什么要用 TensorBoard 进行超参数优化?
一张图胜过千言万语,这也适用于复杂的深度学习模型。深度学习模型被认为是一个黑匣子,你发送一些输入数据,模型进行一些复杂的计算,瞧,你现在有你的结果了!!!
TensorBoard 是 Tensorflow 的一个可视化工具包,用于显示不同的指标、参数和其他可视化内容,帮助调试、跟踪、微调、优化和共享您的深度学习实验结果
有了 TensorBoard,可以在每个历元跟踪模型的精度和损耗;并且还具有不同的超参数值。超参数不同值的跟踪精度将帮助您更快地微调模型。
最后,下面是用 Python 实现的代码……
我们将使用 TensorBoard 使用猫狗数据集可视化标量、图形和分布。
导入所需的库
导入 TensorFlow 和 TensorBoard HParams 插件以及 Keras 库,用于预处理图像和创建模型。
**import tensorflow as tf
from tensorboard.plugins.hparams import api as hp
import datetime
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Conv2D, Flatten, Dropout, MaxPooling2D
from tensorflow.keras.preprocessing.image import ImageDataGenerator, img_to_array, load_img
import numpy as np**
我用过 TensorFlow 2.0.0 版本。
加载 TensorBoard 笔记本扩展
# Load the TensorBoard notebook extension
**%load_ext tensorboard**
创建图像分类深度学习模型
设置培训的关键参数
**BASE_PATH = 'Data\\dogs-vs-cats\\train\\'
TRAIN_PATH='Data\\dogs-vs-cats\\train_data\\'
VAL_PATH='Data\\dogs-vs-cats\\validation_data\\'batch_size = 32
epochs = 5
IMG_HEIGHT = 150
IMG_WIDTH = 150**
重新缩放并对训练图像应用不同的增强
**train_image_generator = ImageDataGenerator( rescale=1./255, rotation_range=45, width_shift_range=.15, height_shift_range=.15, horizontal_flip=True, zoom_range=0.3)**
重新调整验证数据
**validation_image_generator = ImageDataGenerator(rescale=1./255)**
为训练和验证数据集生成批量归一化数据
**train_data_gen = train_image_generator.flow_from_directory(batch_size = batch_size, directory=TRAIN_PATH, shuffle=True, target_size=(IMG_HEIGHT, IMG_WIDTH), class_mode='categorical')val_data_gen = validation_image_generator.flow_from_directory(batch_size = batch_size, directory=VAL_PATH, target_size=(IMG_HEIGHT, IMG_WIDTH), class_mode='categorical')**
为网格搜索设置超参数
我们通过列出超参数的不同值或值范围,使用四个超参数来运行我们的实验。
对于离散的超参数,会尝试所有可能的参数组合,对于实值参数,只会使用上下界。
- 第一密集层单元数:256 和 512
- 辍学率:范围在 0.1 到 0.2 之间。因此将使用 0.1 和 0.2 的辍学率。
- 优化者:亚当、SGD 和 rmsprop
- 优化器的学习率:0.001,0.0001,0.0005,
我们还将指标设置为显示在 TensorBoard 上的精确度
## Create hyperparameters
**HP_NUM_UNITS=hp.HParam('num_units', hp.Discrete([ 256, 512]))
HP_DROPOUT=hp.HParam('dropout', hp.RealInterval(0.1, 0.2))
HP_LEARNING_RATE= hp.HParam('learning_rate', hp.Discrete([0.001, 0.0005, 0.0001]))
HP_OPTIMIZER=hp.HParam('optimizer', hp.Discrete(['adam', 'sgd', 'rmsprop']))****METRIC_ACCURACY='accuracy'**
创建和配置日志文件
**log_dir ='\\logs\\fit\\' + datetime.datetime.now().strftime('%Y%m%d-%H%M%S')
with tf.summary.create_file_writer(log_dir).as_default():
hp.hparams_config(
hparams=
[HP_NUM_UNITS, HP_DROPOUT, HP_OPTIMIZER, HP_LEARNING_RATE],
metrics=[hp.Metric(METRIC_ACCURACY, display_name='Accuracy')],
)**
创建、编译和拟合模型
超参数不是硬编码的,而是取自 hparams 字典的不同参数:HP _ dropout 对于 dropout,HP_NUM_UNITS 对于第一个密集层中的单元数,HP_OPTIMIZER 设置不同的优化器。我们采用使用的优化器,并根据 HP_LEARNING_RATE 设置学习率。
该函数返回最后一个纪元的验证精度。
**def create_model(hparams):
model = Sequential([
Conv2D(64, 3, padding='same', activation='relu',
input_shape=(IMG_HEIGHT, IMG_WIDTH ,3)),
MaxPooling2D(),**
#setting the Drop out value based on HParam
**Dropout(hparams[HP_DROPOUT]),
Conv2D(128, 3, padding='same', activation='relu'),
MaxPooling2D(),
Dropout(hparams[HP_DROPOUT]),
Flatten(),
Dense(hparams[HP_NUM_UNITS], activation='relu'),
Dense(2, activation='softmax')])**
#setting the optimizer and learning rate
**optimizer = hparams[HP_OPTIMIZER]
learning_rate = hparams[HP_LEARNING_RATE]
if optimizer == "adam":
optimizer = tf.optimizers.Adam(learning_rate=learning_rate)
elif optimizer == "sgd":
optimizer = tf.optimizers.SGD(learning_rate=learning_rate)
elif optimizer=='rmsprop':
optimizer = tf.optimizers.RMSprop(learning_rate=learning_rate)
else:
raise ValueError("unexpected optimizer name: %r" % (optimizer_name,))**
# Comiple the mode with the optimizer and learninf rate specified in hparams
**model.compile(optimizer=optimizer,
loss='categorical_crossentropy',
metrics=['accuracy'])**
#Fit the model
**history=model.fit_generator(
train_data_gen,
steps_per_epoch=1000,
epochs=epochs,
validation_data=val_data_gen,
validation_steps=1000,
callbacks=[
tf.keras.callbacks.TensorBoard(log_dir), # log metrics
hp.KerasCallback(log_dir, hparams),# log hparams
])
return history.history['val_accuracy'][-1]**
对于模型的每次运行,记录带有超参数和最终历元精度的 hparams 摘要。我们需要将最后一个时期的验证精度转换为标量值。
**def run(run_dir, hparams):
with tf.summary.create_file_writer(run_dir).as_default():
hp.hparams(hparams)** # record the values used in this trial
**accuracy = create_model(hparams)**
#converting to tf scalar
**accuracy= tf.reshape(tf.convert_to_tensor(accuracy), []).numpy()
tf.summary.scalar(METRIC_ACCURACY, accuracy, step=1)**
使用不同的超参数值运行模型
这里的实验使用网格搜索,并测试第一层的单元数、辍学率、优化器及其学习率的超参数的所有可能组合,精确度用于精确度。
**session_num = 0****for num_units in HP_NUM_UNITS.domain.values:
for dropout_rate in (HP_DROPOUT.domain.min_value, HP_DROPOUT.domain.max_value):
for optimizer in HP_OPTIMIZER.domain.values:
for learning_rate in HP_LEARNING_RATE.domain.values:
hparams = {
HP_NUM_UNITS: num_units,
HP_DROPOUT: dropout_rate,
HP_OPTIMIZER: optimizer,
HP_LEARNING_RATE: learning_rate,
}
run_name = "run-%d" % session_num
print('--- Starting trial: %s' % run_name)
print({h.name: hparams[h] for h in hparams})
run('logs/hparam_tuning/' + run_name, hparams)
session_num += 1**
HParams 仪表板中结果的可视化
您可以使用不同的命令查看 HParams TensorBoard 仪表板:在 Jupyter notebook 中或使用 cmd
使用 cmd
您将通过使用以下命令提供存储不同运行日志的目录路径来显示 Hparam 仪表板
python -m tensorboard.main --logdir="logs/hparam_tuning"
当按降序对准确度排序时,可以看到优化最多的模型是 256 个单元,辍学率为 0.2,rmsprop 优化器的学习率为 0.0005。
使用 Jupyter 笔记本
%tensorboard --logdir='\logs\hparam_tuning'
您也可以查看平行坐标视图,显示每个超参数的单次运行并显示精确度
Tensorboard Hparams 仪表板有助于找到最佳的超参数,以获得最佳的模型精度
结论:
张量板超参数调整提供了一种直观的方式来了解哪些超参数可用于微调深度学习模型以获得最佳精度
参考资料:
https://github.com/tensorflow/tensorboard/issues/3688
https://www . tensor flow . org/tensor board/hyperparameter _ tuning _ with _ hparams
TensorFlow 2.0: tf.function 和亲笔签名
随着 TensorFlow (TF) 2.0 的出现,tf.function
的引入为 TF 1.0 带来了一些有用的改进,最显著的是 AutoGraph 的引入。
TensorFlow 是如何工作的?
基本上,TensorFlow 通过计算图运行,即节点图用于表示一系列 TensorFlow 操作。
然而,编译张量流图可能是一个繁琐的过程,因为语法结构与 Python 明显不同,并且图代码比简单的 Python 结构使用更多的资源。
AutoGraph 的目的是通过把用 Python 的经典语法结构编写的代码转换成 TensorFlow 图兼容代码来图形化代码。这使得使用 Python 和 TensorFlow 更加直观,因为它简化了从 Python 代码到图形代码的转换。
更重要的是,tf.function
允许直观地使用急切执行和自动签名,从而可以使用 Python 语法运行一个函数,然后转换成等价的图形代码。
我们举个例子。
急切执行:循环函数的区域
这是一个计算圆面积的 Python 函数。在 TF 2.0 中默认开启急切执行,值 r (圆的半径)被定义为tf.Variable
。
>>> import tensorflow as tf
>>> tf.executing_eagerly()
>>> r = tf.Variable(10.0, name="r")>>> def area(r):
>>> circle=3.14*(r**2.00)
>>> return circle>>> area(10)314.0>>> print('Areas of respective circles: %2.2f, %2.2f, %2.2f' % (area(tf.constant(50.0)), area(tf.constant(100.0)), area(tf.constant(150.0))))Areas of respective circles: 7850.00, 31400.00, 70650.00
用 tf 签名。会议
现在,假设有人希望使用 AutoGraph 实现这个代码。使用 TF 1.0 约定,这可能是使用tf.Session
实现的。以前,图形必须在会话中显式启动才能运行。
>>> r = tf.Variable(10.0, name="r")>>> def area(r):
>>> circle=3.14*(r**2.00)
>>> return circle>>> print(tf.autograph.to_code(area))def tf__area(r):
do_return = False
retval_ = ag__.UndefinedReturnValue()
circle = 3.14 * r ** 2.0
do_return = True
retval_ = circle
cond = ag__.is_undefined_return(retval_) def get_state():
return () def set_state(_):
pass def if_true():
retval_ = None
return retval_ def if_false():
return retval_
retval_ = ag__.if_stmt(cond, if_true, if_false, get_state, set_state)
return retval_
这是一个图形格式的代码示例。
>>> tf_area = tf.autograph.to_graph(area)
>>> tf_area<function __main__.create_converted_entity_factory.<locals>.create_converted_entity.<locals>.tf__area(r)>>>> print(tf_area)<function create_converted_entity_factory.<locals>.create_converted_entity.<locals>.tf__area at 0x7f7b57f8a620>>>> with tf.Graph().as_default():
a1 = tf_area(tf.constant(50.0))
a2 = tf_area(tf.constant(100.0))
a3 = tf_area(tf.constant(150.0))
with tf.compat.v1.Session() as sess:
print('Areas of respective circles (graph results): %2.2f, %2.2f, %2.2f\n' % (sess.run(a1), sess.run(a2), sess.run(a3)))Areas of respective circles (graph results): 7850.00, 31400.00, 70650.00
使用会话意味着上面计算的圆的面积值将只在会话本身内计算。
使用 tf.function 自动签名
让我们看看如何使用tf.function
来实现这一点。
>>> r = tf.Variable(10.0, name="r")>>> @tf.function
>>> def area(r):
>>> circle=3.14*(r**2.00)
>>> return circle>>> print(tf.autograph.to_code(area.python_function))def tf__area(r):
do_return = False
retval_ = ag__.UndefinedReturnValue()
circle = 3.14 * r ** 2.0
do_return = True
retval_ = circle
cond = ag__.is_undefined_return(retval_) def get_state():
return () def set_state(_):
pass def if_true():
retval_ = None
return retval_ def if_false():
return retval_
retval_ = ag__.if_stmt(cond, if_true, if_false, get_state, set_state)
return retval_>>> area(60)<tf.Tensor: id=13, shape=(), dtype=float32, numpy=11304.0>>>> print(area(tf.constant(50.00)), area(tf.constant(100.0)), area(tf.constant(150.0)))tf.Tensor(7850.0005, shape=(), dtype=float32) tf.Tensor(31400.002, shape=(), dtype=float32) tf.Tensor(70650.0, shape=(), dtype=float32)
从上面可以看出,计算面积值不需要会话。而是用tf.function
指定可以直接转换为 AutoGraph 的函数,然后面积值计算为张量。
结论
在这个例子中,我们看到了 TF 2.0 如何通过使用tf.function
和 AutoGraph 来简化 TensorFlow 中的函数实现。非常感谢您的时间,您还可以在 michael-grogan.com找到更多使用 TensorFlow 和 Keras 的机器学习示例。
免责声明:本文是在“原样”的基础上编写的,没有任何担保。本文旨在提供数据科学概念的概述,不应以任何方式解释为专业建议。
TensorFlow 2.1:操作指南
我们深入吧!—Dids的惊人画面
keras 模式、渴望模式和图形模式
如果你和我一样,是一个正常人,你可能已经经历过有时你是如何陷入你的应用程序的开发中,以至于很难找到一个时刻停下来思考我们是否以最有效的方式做事:我们是否使用了正确的工具?哪个框架最适合我的用例?这种方法可扩展吗?我们考虑到可伸缩性了吗?
在 AI 领域更是如此。我们都知道人工智能是一个快速发展的领域。每天都有新的研究发表。正在高速开发的主要人工智能框架之间存在巨大的竞争。新的硬件架构、芯片和优化被发布以支持日益增长的人工智能应用的部署…然而,尽管有所有的华而不实,有时你需要停下来重新考虑。
什么时候是停下来重新考虑的好时机?只有你会知道。对我来说,这一刻是最近才到来的。自从我进入这个领域以来,我一直在工作和个人项目中使用 Keras 和 Tensorflow 1.x (TF1)。我完全喜欢 Keras 库的高级方法和 Tensorlfow 的低级方法,当您需要更多定制时,它们可以让您在引擎盖下进行更改。
尽管我是 Keras-Tensorflow 婚姻的超级粉丝,但总有一个非常具体的负面因素让这对夫妇远离田园生活:调试功能。正如你已经知道的,在 Tensorflow 中,有一个先定义计算图,然后编译它(或者移动到 GPU)然后非常高效地运行它的范例。这种范式非常好,从技术上讲也很有意义,但是,一旦你在 GPU 中有了模型,就几乎不可能调试它。
这就是为什么在 TensorFlow 2.0 的 alpha 版本发布大约一年后,我决定尝试 TensorFlow 2.1(我可以从 TF2.0 开始,但我们都知道我们喜欢新软件),并与您分享它的进展。
张量流 2.1
令人难过的事实是,我很难弄清楚我应该如何使用这个新的 TensorFlow 版本,即著名的 2.1 稳定版本。我知道,这里有大量的教程,笔记本和代码手册……然而,我发现困难并不在于编程,因为归根结底,这只是 Python,而是范式的转变。简而言之:TensorFlow 2 编程不同于 TensorFlow 1,就像面向对象编程不同于函数式编程一样。
在做了一些实验后,我发现在 TensorFlow 2.1 中有 3 种建模方法:
- Keras 模式(
tf.keras
):基于图形定义,稍后运行图形。 - 急切模式:基于定义一个迭代地执行定义一个图的所有操作。
- 图表模式(
tf.function
):之前两种方法的混合。
无聊到此为止。给我看看代码!
Keras 模式
这是我们都习惯的标准用法。只使用普通的 Keras 和自定义损失函数,以平方误差损失为特征。该网络是一个 3 密集层深度网络。
# The network
x = Input(shape=[20])
h = Dense(units=20, activation='relu')(x)
h = Dense(units=10, activation='relu')(h)
y = Dense(units=1)(h)
这里的目标是教一个网络学习如何对一个 20 个元素的向量求和。因此,我们向网络输入一个数据集[10000 x 20]
,即 10000 个样本,每个样本有 20 个特征(要求和的元素)。那是在:
# Training samples
train_samples = tf.random.normal(shape=(10000, 20))
train_targets = tf.reduce_sum(train_samples, axis=-1)
test_samples = tf.random.normal(shape=(100, 20))
test_targets = tf.reduce_sum(test_samples, axis=-1)
我们可以运行这个例子,得到通常漂亮的 Keras 输出:
Epoch 1/10
10000/10000 [==============================] - 10s 1ms/sample - loss: 1.6754 - val_loss: 0.0481
Epoch 2/10
10000/10000 [==============================] - 10s 981us/sample - loss: 0.0227 - val_loss: 0.0116
Epoch 3/10
10000/10000 [==============================] - 10s 971us/sample - loss: 0.0101 - val_loss: 0.0070
这里发生了什么?嗯,没什么,只是一个 Keras 玩具的例子训练在 10 秒每时代(在一个英伟达 GTX 1080 Ti)。编程范式呢?和之前一样,就像在 TF1.x 中,你定义了图形,然后你通过调用keras.models.Model.fit
来运行它。调试功能呢?和以前一样…没有。你甚至不能在损失函数中设置一个简单的断点。
运行完这段代码后,您可能会疑惑一个非常明显的问题:TensorFlow 2 版本承诺的所有优秀特性都到哪里去了?你是对的。如果与 Keras 包的集成意味着不需要安装额外的包,那么优势是什么?
除此之外,还有一个更重要的问题:众所周知的调试特性在哪里?幸运的是,这是急切模式来拯救。
渴望模式
如果我告诉你有一种方法可以交互地构建你的模型,并且可以访问运行时的所有操作,那会怎么样?— 如果你激动得发抖,这意味着你已经经历了 10 个纪元后随机批次运行时错误的深刻痛苦……是的,我知道,我也去过那里,我们可以在那些战斗后开始称自己为战友。
嗯,是的,这就是你要找的操作模式。在这种模式下,所有的张量操作都是交互式的,你可以设置一个断点并访问任何中间张量变量。然而,这种灵活性是有代价的:更显式的代码。让我们来看看:
阅读完代码后,你脑海中出现的第一件事可能是:许多代码只是为了做一个model.compile
和一个model.fit
。是的,没错。但另一方面,你可以控制之前发生的一切。引擎盖下发生了什么?训练循环。
所以现在情况变了。在这种方法中,你可以从头开始设计事情如何进行。以下是您现在可以指定的内容:
- 度量:曾经想要测量每个样本、批次或任何其他自定义统计的结果吗?没问题,我们掩护你。现在,您可以使用传统的移动平均线或任何其他自定义指标。
- 损失函数:曾经想做疯狂的多参数依赖损失函数?嗯,这也解决了,你可以在损失函数定义中得到所有你想要的技巧,而不用 Keras 用它的
_standarize_user_data
( 链接)来抱怨它 - 梯度:您可以访问梯度,并定义向前和向后传递的细节。是的,最后,请和我一起欢呼吧!
这些指标是用新的tf.keras.metrics
API 指定的。您只需获取想要的指标,定义它并像这样使用它:
# Getting metric instanced
metric = tf.keras.metrics.Mean() # Run your model to get the loss and update the metric
loss = [...]
metric(loss)# Print the metric
print('Training Loss: %.3f' % metric.result().numpy())
损失函数和梯度分别在正向和反向过程中计算。在这种方法中,向前传球必须由tf.GradientTape
记录。tf.GradientTape
将跟踪(或记录)前向传递中完成的所有张量操作,因此它可以计算后向传递中的梯度。换句话说:为了向后跑,你必须记住你向前走过的路。
# Forward pass: needs to be recorded by gradient tape
with tf.GradientTape() as tape:
y_pred = model(x)
loss = loss_compute(y_true, y_pred)# Backward pass:
gradients = tape.gradient(loss, model.trainable_weights)
optimizer.apply_gradients(zip(gradients, model.trainable_weights))
这非常简单,在向前传递中,你运行你的预测,并通过计算损失来看你做得有多好。在反向过程中,通过计算梯度来检查权重如何影响损失,然后通过更新权重(在优化器的帮助下)来尝试最小化损失。
您还可以在代码中注意到,在每个时期结束时,会计算验证损失(通过只运行正向传递而不更新权重)。
让我们看看这与之前的方法相比如何(我将输出减少了一点,这样它就可以放在这里):
Epoch 1:
Loss: 1.310: 100%|███████████| 10000/10000 [00:41<00:00, 239.70it/s]
Epoch 2:
Loss: 0.018: 100%|███████████| 10000/10000 [00:41<00:00, 240.21it/s]
Epoch 3:
Loss: 0.010: 100%|███████████| 10000/10000 [00:41<00:00, 239.28it/s]
发生了什么事?你注意到了吗?在同一台机器上,每个时期花费了 41 秒,即 4 倍的时间增量…这只是一个虚拟模型。你能想象这对一个真实的用例模型,如 RetinaNet、YOLO 或 MaskRCNN,会有多大的影响吗?
幸运的是,优秀的 TensorFlow 人员意识到了这一点,并实现了图形模式。
图表模式
图表模式(来自 AutoGraph 或tf.function
)是前两种模式的混合模式。这里的这里的和这里的这里的你可以大致了解一下这是什么。但是我发现那些指南有点混乱,所以我用我自己的话来解释。
如果 Keras 模式是关于定义图形并稍后在 GPU 中运行它,而 eager 模式是关于交互式地执行每个步骤,那么 graph 模式允许您像在 eager 模式中一样编码,但运行训练的速度几乎与在 Keras 模式中一样快(所以是的,在 GPU 中)。
关于 eager 模式的唯一变化是,在 graph 模式中,您将代码分解成小函数,并用@tf.function
对这些函数进行注释。让我们来看看事情是如何变化的:
现在你可以看到前向和后向传递计算是如何被重构为两个用@tf.function
装饰器注释的函数的。
那么这里到底发生了什么?简单。每当你用@tf.function
decorator 注释一个函数时,你就像 Keras 一样将这些操作“编译”到 GPU 中。因此,通过注释您的函数,您可以告诉 TensorFlow 在 GPU 中的优化图形中运行这些操作。
在引擎盖下,真正发生的是函数正在被亲笔、tf.autograph
解析。AutoGraph 将获取函数输入和输出,并从它们生成一个张量流图,这意味着,它将解析操作,以将输入的输出转换为张量流图。这个生成的图形将非常高效地运行到 GPU 中。
这就是为什么它是一种混合模式,因为除了用@tf.function
装饰器注释的操作之外,所有的操作都是交互运行的。
这也意味着你可以访问所有的变量和张量,除了用@tf.function
修饰的函数中的变量和张量,你只能访问它的输入和输出。这种方法建立了一种非常清晰的调试方式,在这种方式下,您可以在渴望模式下开始交互式开发,然后,当您的模型准备就绪时,使用@tf.function
将其推向生产性能。听起来不错吧?让我们看看进展如何:
Epoch 1:
Loss: 1.438: 100%|████████████| 10000/10000 [00:16<00:00, 612.3it/s]
Epoch 2:
Loss: 0.015: 100%|████████████| 10000/10000 [00:16<00:00, 615.0it/s]
Epoch 3:
Loss: 0.009: 72%|████████████| 7219/10000 [00:11<00:04, 635.1it/s]
嗯,一个惊人的 16s/纪元。您可能认为它不如 Keras 模式快,但另一方面,您可以获得所有的调试功能和非常接近的性能。
结论
如果您一直在关注这篇文章,那么您将不会感到惊讶,所有这些最终都归结为一个非常古老的软件问题:灵活性还是效率?渴望模式还是 Keras 模式?为什么要和解?使用图形模式!
在我看来,TensorFlow 的工作人员在为我们这些开发人员提供更多灵活性方面做得非常出色,而且没有在效率方面做出太大的牺牲。所以从我的立场来看,我只能为他们说 bravo 。
TensorFlow 2.2+和自定义培训逻辑
最近的 TensorFlow 版本引入了 tf.keras.Model 的新方法 train_step 和 test_step ,极大地改进了我们处理自定义训练循环的方式。我会告诉你怎么做。
来源:pexels.com
大家都熟悉标准模型——一个输入、一个输出、一个损失函数。也许还有几个指标。你可以用顺序或功能的方式定义模型,编译并训练它。
有时候,会有多个输入。或多路输出。这仍然可以用这些 API 来实现。假设输出是“独立的”——损失函数(一个用于所有输出或一个用于每个输出)分别对每个输出起作用。
有时候,我们需要更进一步。也许损失是从更多的输出中计算出来的。或者有一些用于计算损失的附加信息。这是必须实现定制训练循环的时候。
在这篇博文中,我不会涉及这样的具体用例。相反,我将向您展示一个简化的示例,说明如何训练它的三种方式。稍后,我将继续一个更复杂的例子。敬请关注!
示例问题
即使在教程里,我也喜欢可运行的代码,甚至现实世界的问题。因此,让我们以 TensorFlow 数据集之一—bean为例。
Beans 是一个使用智能手机相机在田间拍摄的豆子图像数据集。它包括 3 个类别:2 个疾病类别和健康类别。描述的疾病包括角斑病和豆锈病。乌干达国家作物资源研究所的专家对数据进行了注释,数据由 Makerere AI 研究实验室收集。
它相当小(171.63 MiB,1295 张图片),使我们能够快速训练我们的模型。即使经过少量的时期,结果看起来也是有希望的。
来自 beans 数据集的示例图像和标签,来源tensorflow.org
这里是后面三章的通用代码,主要包含数据集的准备和训练设置。这与我们的主题没有直接联系,我不想让文章太长,所以如果你愿意,请查看随附的要点。
标准编译和拟合方法
这就是如何定义和训练一个简单的转移模型。没什么神秘的,但为了完整起见我会在这里展示一下。
像往常一样训练。关于导入、参数和数据集定义,参见该代码。
我使用了 MobileNet 和几个附加层。globalaveragepool2d将要素转换为单个矢量,该矢量可用于最终的密集图层。中间我用了辍学。请记住,数据集非常小,我们必须小心过度拟合。剩下的就简单了。
用老方法定制训练逻辑
自定义训练循环的详细描述可在官方文档中找到。如果我们将这一点转化为上一段中的问题,我们会得到以下代码:
自定义训练循环。关于导入、参数和数据集定义,参见该代码。
你看到所有的代码,只是关心跟踪损失和准确性?现在假设您有多个指标和几个回调,例如包括 TensorBoard。你想要更好的输出。代码开始越来越多的扩展。
未来:train_step()和 test_step()
从 TensorFlow 2.2 开始,不再需要所有这些锅炉板代码。你只需要告诉 TensorFlow 每个单独的训练步骤(可能还有测试步骤)将会是什么样子。其余的在 tf.keras.Model 类中完成。我们就不拐弯抹角了,下面是代码:
使用 train_step()和 test step()的示例。关于导入、参数和数据集定义,参见该代码。
我想指出两件事:
- 输入的格式通常是 x,y 和可选的样品重量。您不需要在子类中遵守这一点。不过要小心,这是有后果的。很明显,您不能再使用 fit 方法的
class_weight
参数。即使您只向这三个参数添加了额外参数,也是如此。 - 您可以使用已编译的 loss(损失)、metrics 和 optimizer。我建议你尽可能经常这样做。但是通常,当这些还不够时,您会覆盖这个方法。所以不要害怕从一个
compile
方法中去掉一些,定义你自己的或者完全离开compile()
。
没有编译的优化器、损失和指标
那么当你不考虑compile()
的时候会发生什么呢?您仍然需要添加一些代码。但是请注意与完全定制的训练循环的巨大差异。您现在正在指定一个步骤的行为。因此,父模型类可以负责所有的支持功能。例如打印漂亮的进度条或者调用所有的回调函数。(它们可以以通常的方式作为参数提供给fit
方法。)
我试图将所有重要的注释写入代码本身。因此,我在此仅补充一点。可以省略metric()
属性,直接返回train_step()
和test_step()
中的 name: (float) value 对。进度输出将是正常的,你会看到一个平均值。但不是在你的试镜中。在那里,您将获得与您返回的值完全相同的值。
最后的想法
在这篇博文中,我向您介绍了在 TensorFlow 中训练模型的不同方法,包括覆盖 Keras 模型的新方法train_step
和test_step
。这个例子使用起来太简单了。但我希望它能达到演示不同方法的目的,而不需要解释复杂的模型。我想做的比一些即将到来的文章。
如果你觉得这有用,请给我留言。如果你有任何问题,请随时这样做。我很乐意回答你。
张量流和图像增强
是的,我知道。你已经听说过很多图像增强。但是你猜怎么着!无论如何我都想给你一个惊喜。让我们开始吧!
来源:Pexels.com
A 增强是一种众所周知的防止深度神经网络过度拟合的技术。如果你做得好,你可以增加你的数据集,而不需要收集和注释新数据的高成本。Python 中有很多这方面的库,但我想主要谈谈 TensorFlow。
快速概览
为了获得最佳性能,应该考虑使用 tf.data API。第一眼看上去很好看。使用简单,有效。过了一会儿,你开始想知道图像放大。我如何把它们放进去?
如果你习惯了像imgaug,albuminations甚至 OpenCV ,你需要一个TF . py _ function的帮助。但是一些纯张量流解呢?有些可以编译成图表运行。**
tf .图像
这个 是你可能会看的第一个地方。至少我做到了。有一些非常有用的函数,例如:
- 图像调整—亮度、对比度、饱和度、JPEG 质量等。
- 裁剪。
- 翻转、旋转和换位—向左/向右翻转、向上/向下翻转、旋转 90 度。不幸的是不是完整的旋转。
tfa .图像
图片 包来自tensor flow Addons是另一个你应该定期检查的包。不仅仅是增强,还有附加层、损耗、优化器等等。但是现在回到赛道上。我们有什么?主要是旋转、剪切和一般的平移和变换。
图像检测?
不幸的是,这两个人都对图像检测不感兴趣。是的,你可以翻转图像。但是选择的边界框呢?!我猜你不想失去他们!
来源:Pexels.com
我们的图书馆— tf-image
如果你渴望自己查库,请访问我们的 GitHub 资源库 。全部是开源的。我们只是刚刚开始,所以我们欢迎任何关于你喜欢(不喜欢)或你需要什么的反馈。如果你觉得它有用,我们将非常感谢你的代码贡献!
不同的工作流程
这篇博文旨在介绍这个库,而不是完整的手册或对每个功能的描述。因此,我将仅限于举两个例子。
多合一
为了使增强尽可能简单,有 random_augmentations 函数。您提供图像、增强设置和可选的边界框。剩下的就是函数本身了。此外,增强是以随机顺序执行的,以使该过程更加强大。
很酷,但在某些方面有局限性。主要是这个设置太简单了。每个选项都有许多参数。将来我们会推出更先进的设置。但是现在,您可以使用我们库的所有其他部分轻松创建自己的增强功能。
使用核心功能
使用核心函数也很容易。为了向您展示 random _ function(random _ function _ bbox es),我在下面演示了一个例子。如果你不想每次都应用你的增强,这是一个值得一看的地方。
结论
今天到此为止。如果你已经走了这么远,谢谢你。请,评论,批评,分享,发送合并请求!
我要感谢 Michal Lukac 与我在这个项目上的合作,感谢 Ximilar 给了我这个机会和一个积极的方法来开源我们的一些工作。
Tensorflow 最佳实践:命名输入和输出
根据位置索引和输入值排序退出。开始依赖指定的输入和输出。避免数据布线错误
命名输入和输出本质上是带有字符串关键字和张量值的字典。
利益
- 防止特征重新排序
- 提供签名和元数据的自给自足模型
- 重命名和缺少功能保护
大多数机器学习管道从结构化源(数据库、CSV 文件/ Pandas 数据帧、TF 记录)读取数据,执行特征选择、清理(以及可能的)预处理,将原始多维数组(张量)与表示每个输入样本的正确预测的另一个张量一起传递给模型。
在生产中重新排序或重命名输入特征? → 无效结果或客户端生产中断
缺项特征?缺失数据?产值解读不好?误混整数索引? → 无效结果或客户端生产中断
想知道训练时使用了哪些特征列,以便为推理提供相同的特征列? → 你不能——曲解错误
想知道输出值代表什么值? → 你不能——曲解错误
不要在模型输入层上删除列名。
默认情况下,tf.data.Dataset
已经允许您这样做,将输入视为一个字典。
这些年来,上述问题变得更容易处理了。以下是 Tensorflow 2.x
生态系统中可用解决方案的简要概述。
-
tfRecords 和 TF。示例无疑是用于任何规模深度学习项目的最佳数据格式。默认情况下,每个功能都以命名。
-
Tensorflow Transform 使用指定的输入并产生指定的输出,鼓励你对你的模型做同样的事情。
-
Keras 支持图层字典作为输入和输出
添加多种尺寸的特征很简单:只需为 window_size 添加另一个参数,或者将特征形状与特征名称一起传递。
- TensorSpec 和服务签名定义默认支持命名 IOs。
通过使用这个serving_raw
签名定义,您可以通过 JSON 有效负载直接调用 Tensorflow 服务端点,而无需序列化到tf.Example
。
看看 TF 服务上的metadata
签名,我目前正在做一个比特币预测模式的例子:
最后,如果您正在使用 TFX 或获得了用于输入的协议缓冲模式,您应该使用它来发送用于推断的数据,因为这样会更有效率,并且错误会更快地出现在客户端,而不是服务器端。即使在这种情况下,也要为您的模型使用命名的输入和输出。
感谢一路看完!
还想学习如何正确构建您的下一个机器学习项目吗?
- 查看我的[结构化 ML 管道项目文章](http://Want to create) 。
TensorFlow +类继承=漂亮的代码
用这种方法深入研究更多的技术模型。
简介:
学习开发深度学习模型并不是一件容易完成的任务——尤其是对于那些可能在该领域没有丰富经验的人来说。当开始学习如何在 TensorFlow 中构建 DL 模型时,工作流程通常如下:
但是,如果部署或更多的技术模型出现,这种构建模型的格式可能会对您的时间造成负担,而不是资产。Python 的面向对象特性很好地发挥了作用,使它更多地属于后者而不是前者。本教程将分解开发更健壮的模型的工作流程。
继承:
当观察上面的gist
时,我们可以看到我们初始化了keras.Sequential
,它的类类型是tf.keras.Model
。通过添加一个层列表作为parameter
,我们初始化要调用的层——按顺序——用于向前传递。
我们可以使用所谓的class inheritance
来构建张量流模型。通过继承tf.keras.Model
类,我们可以将我们自己的层合并到模型中,并从那里构建我们的向前传递。
对于简单分类的例子,比如上面的例子,这样做可能有点复杂(双关语。考虑到这一点,我仍然会继续上面的模式。如果您理解 tensor flow API 的基础,那么您将能够看到上面要点中的调用如何转化为开发更多面向对象的代码,这是本文的主要目的。
当继承一个tf.keras.Model
类时,有两个主要步骤需要考虑:
- 在
__init__
内初始化模型中的必要层。 - 在
call
内订购模型的向前传球。
正在初始化类:
对于__init__
,我们有 4 个主要步骤。
- 首先,我们引入
super
的__init__
——在本例中是tf.keras.Model
。 - 其次,我们初始化
Flatten
层。 - 第三,我们用
128
单元初始化Dense
层,并激活tf.nn.relu
。值得注意的是,当我们在第一个要点中调用激活函数时,我们使用了一个字符串('relu'
而不是tf.nn.relu
)。)当构建我们自己的模型类时,我们必须从tf.nn
模块中调用我们的激活函数。 - 第四,我们用
10
单元初始化最后的Dense
层。
为了一致性,总是首先初始化super
的__init__
。这将确保当您在使用中调用该类时,它将作为tf.keras.Model
加载。层的初始化顺序无关紧要。
打电话:
当定义call
时,我们正在定义模型的正向传递。我们有参数inputs
,在拟合时,这些参数将是x_train
。因此,在call
中,它将类似于第一个要点中的层的排序。然而,与层列表相反——并且依赖于fit
来确定向前传递——我们必须明确地确定x
首先进入Flatten
层——也就是self.flatten
,然后将其传递到self.dense1
层,最后输出self.dense2
层的结果。
结论:
本文应该作为构建更多面向对象代码的通用指南。了解自定义模型的工作流程可以让您在研究中获得更多自由和试验。我希望这篇文章对你有益。
感谢您的阅读。
张量流云:从本地到分布式
TensorFlow Cloud 是一个 python 包,用于使用 Google AI 平台从本地调试无缝过渡到云中的分布式训练。TensorFlow Cloud 有在谷歌云平台(GCP)上运行 API 用于训练模型,有 tuner 用于超参数调优。它通过一个单一功能调用简化了云上的训练模型,并处理所有特定于云的任务,如 VM 实例创建和选择分发策略[1]。
调用后的 run API 将验证和预处理 python 脚本或笔记本,使用云构建或本地 docker 守护进程将其容器化,并将训练作业部署到 AI 平台。可以将相关日志流式传输到日志记录,并使用托管 TensorBoard 监控培训。
运行 API 高层流程
**run**(entry_point=None,
requirements_txt=None,
distribution_strategy='auto',
docker_base_image=None,
chief_config='auto',
worker_config='auto',
worker_count=0,
entry_point_args=None,
stream_logs=False,
docker_image_bucket_name=None,
job_labels=None,
**kwargs)
运行 API 有以下重要参数,这些参数有助于将代码从本地迁移到 AI 平台。[2]
- entry_point: 可选字符串、 python 脚本或包含训练代码的笔记本。如果未提供 entry_point,则当前 python 脚本或笔记本将被用作 entry_point。例如 train.py 或 train.ipynb
- requirements_txt: 可选字符串,附加必需库列表。
- distribution_strategy: 自动或无,默认为自动,将根据主管配置、员工配置和员工人数选择分配策略。
- chief_config: 分布式集群中主要工作者的机器配置。默认:T4_1X
- worker_config: 分布式集群中工作者的机器配置。默认:T4_1X
- worker_count: 分布式集群中的工作线程数。默认值:0
- entry _ point _ args:entry _ point 脚本或笔记本的可选参数。默认值:无
- stream_logs: 来自 AI 平台的流日志的布尔标志。
- docker _ image _ bucket _ name:可选字符串,指定 docker image 云存储桶名。如果设置了该参数,docker 容器化使用Google Cloud Build完成,否则使用本地 docker 守护进程。
TensorFlow 云培训分发策略
分销策略
运行 API 支持自动和自定义分发策略,上面的流程图显示了自动选择分发策略。所有分布策略都遵循数据并行性,其中模型变量跨设备复制,并使用 all-reduce 策略进行同步。
下图显示了“all reduce 策略(ring all-reduce)的一种可能实现,其中 N 个节点中的每个节点与其两个对等节点通信 2(N-1)次。在此通信期间,节点发送和接收数据缓冲区的块。在前 N-1 次迭代中,接收到的值被添加到节点缓冲区中的值。在第二个 N-1 次迭代中,接收到的值替换保存在节点缓冲区中的值。* [ 5
环环全减策略:图片来自优步·霍罗沃德
一种设备策略:无分布式训练,这种策略的典型用途可能是训练代码验证。
镜像策略:变量在所有设备上复制,并使用 all-reduce 策略保持同步。如果在策略的构造器参数中没有指定设备,那么它将使用所有可用的 GPU。如果没有找到 GPU,它将使用可用的 CPU。
多工作者镜像策略:类似于镜像策略,在所有工作者的每个设备上创建模型中所有变量的副本,并使用 CollectiveOps 的多工作者 all-reduce 实现来保持变量同步。
工作示例
让我们看一个工作示例,通过在 MNIST 数据集上定义一个简单的用于数字分类的 Keras 模型训练代码,保存为 toy_mnist.py [2]
import tensorflow as tf(x_train, y_train), (_, _) = tf.keras.datasets.mnist.load_data()x_train = x_train.reshape((60000, 28 * 28))
x_train = x_train.astype('float32') / 255model = tf.keras.Sequential([
tf.keras.layers.Dense(512, activation='relu', input_shape=(28 * 28,)),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(10, activation='softmax')
])model.compile(loss='sparse_categorical_crossentropy',
optimizer=tf.keras.optimizers.Adam(),
metrics=['accuracy'])model.fit(x_train, y_train, epochs=10, batch_size=128)
上面的模型可以通过编写下面的简单脚本在云上运行。
import tensorflow_cloud as tfc
tfc.run(entry_point='**toy_mnist.py**', chief_config=tfc.COMMON_MACHINE_CONFIGS['T4_4X'])
当我们运行上面的脚本时,用 chief_config 作为 T4_4X 机器,没有创建 worker,因为 worker_count 默认为 0,并且低于自动生成的代码以及其他参数,如 chief_config、worker_config,docker registry 被部署到 AI 平台,并且由于 chief config 的加速器数量> 0T4 _ 4X,选择了TF . distribute . mirroredstrategy。用于分发策略的 TensorFlow 使用NCCL(Nvidia Collective communication s Library)作为 GPU 的跨设备通信,并使用 tf.distribute.NcclAllReduce 作为镜像策略。
import os
import tensorflow as tf
os.environ["TF_KERAS_RUNNING_REMOTELY"]="1"
**strategy = tf.distribute.MirroredStrategy()**
tf.distribute.experimental_set_strategy(strategy)
exec(open("toy_mnist.py").read())
下图显示了提交到 AI 平台的作业。
提交的工作细节,谷歌人工智能平台
训练日志和详细信息在堆栈驱动程序日志中提供,如下图所示,也可以通过在 run API 调用中将 stream_logs 设置为 True 来传输到主机。
显示每个历元的交叉熵损失、精确度的日志
还可以使用**tf.keras.models.save_model**
将模型保存到 Google 云存储桶,使用[**gcloud ai-platform predict**](https://cloud.google.com/sdk/gcloud/reference/ai-platform/predict)
将模型部署到 AI 平台进行在线推理。
关于设置 TensorFlow、TensorFlow-Cloud 和云配置(创建项目和启用 AI 平台等),请参考参考资料[ 3 ]。
参考
- https://www . tensor flow . org/guide/keras/training _ keras _ models _ on _ cloud
- https://github.com/tensorflow/cloud
- https://github.com/tensorflow/cloud#setup-instructions
- https://www.tensorflow.org/api_docs/python/tf/distribute
- https://eng.uber.com/horovod/
TensorFlow 开发峰会 2020 倒带
作为 4 年一度的 TensorFlow 开发者,Summit 2020 发生在三月。因此,这篇文章旨在让这个美妙的人工智能社区的所有工程师、开发人员、计算机科学家和数据科学家了解在那次峰会上做出的最新更新和公告。
截图来自开发者峰会 2020 YouTube 视频[( CC BY 4.0)],部分由作者通过 Canva 创作。
“我们选择去月球”
让我带你回到 1962 年 9 月 12 日,美国第 35 任总统约翰·肯尼迪“我们选择去月球”在莱斯大学的演讲。肯尼迪说。“我们选择在这十年登上月球并做其他事情,不是因为它们容易,而是因为它们很难。”
嗯,我认为 TensorFlow 工程团队将这个演讲放在了心上,在收到来自整个社区的大量反馈后,他们开始致力于解决这个难题,即让 TensorFlow 对世界上基本了解计算机工作原理的每个人或那些能够利用 tensor flow platform 等超能力并使世界变得更美好的人来说尽可能简单。
在这方面,TensorFlow 团队去年实现了一个重要的里程碑,推出了 TensorFlow 2.0 ,这使他们朝着打造易于使用的端到端"一体式"平台的目标又迈进了一步。
建立势头:
TensorFlow 2.0 推出时具有以下特性:
- 简化 API
- 默认情况下,使用 Keras 和 Eager execution 轻松构建模型。
- 支持将模型部署到更多设备上(例如 Raspberry Pi )。
- 强大的研究实验(如 Tensorboard.dev )。
因此,在去年发布的 2.0 版本的基础上,TensorFlow 团队发布了最新版本的 TensorFlow 2.2,其增强功能包括:
- 与上一版本相比,性能有所提高。
- 与 TF 生态系统其余部分的兼容性。
因此,所有那些在迁移到 2.0 时遇到困难的用户,不用担心,因为 TF 团队现在已经使整个系统与你最喜欢的库兼容了。简而言之,适用于 1.x 的也适用于 2.x。
3.最后,任何软件都是根据可伸缩性和稳定性来判断的。显然,TF 团队提交了核心库,这证实了他们不会很快做出任何重大改变。因此,核心库更加稳定。
张量流生态系统
截图自开发者峰会 2020 YouTube 视频[( CC BY 4.0)]
自从拥有 6000 万参数的 Alexnet 通过赢得ImageNet challenge ils vrc 2012而普及深度卷积神经网络以来,总的趋势是制造更深更复杂的网络,以实现更高的精度。
同样,为了提高我们对 TensorFlow 2.x 理解的准确性,我们也需要更深入地挖掘。
A.研究用张量流
TensorFlow 中存在大量的库和扩展,如 JAX 、 TF 概率和张量流量子,这加强了对 ML 世界的研究。与其他平台相比,研究人员更喜欢 TensorFlow,因为它具有新的可扩展性,允许在核心执行,并保持类似于 NumPy ,非常 Python 化,并易于与 Python 生态系统的其余部分集成。
然而,在本节中,我将简要地谈谈我最喜欢的一个特性,即引入了一个名为性能分析器的新工具。
来自 TensorFlow ( CC BY 4.0)中“使用分析器优化 TensorFlow 性能”指南部分的截图
TensorBoard 中的这个新工具将在两个方面帮助研究人员:
- 它将在主机(CPU)、设备(GPU)或主机和设备的组合上针对 TF 模型中的各种张量流运算(ops)持续监控 TF 模型性能。
- 它将为研究人员提供深入的调试指导,最终有助于解决性能瓶颈。
最终结果:它将使模型执行得更快。[对每个人都是双赢]
B.生产张量流
TensorFlow 不仅是研究团体的首选,也是医疗保健和汽车等各行业领域无可争议的选择。由于 TensorFlow 为研究人员提供了尽快测试一些新想法的能力,类似地,它也使实业家或生产工程师能够采纳他们的想法,并通过使培训到部署阶段变得无障碍来创造现实世界的影响。
因此,让我们了解更多关于 TensorFlow 2.x 更新和库的信息,以实现我们的端到端 ML 目标。
截图自开发者峰会 2020 YouTube 视频[( CC BY 4.0)]
TensorFlow 和 Keras 的婚姻万岁
使用 Canva 创建图像,图像元素包括来自[【pngriver.com】]、TensorFlow 和 Keras 站点(CC BY 4.0)的来源
包括我在内的整个人工智能界都喜欢使用 Keras ,因为它易于使用,甚至允许它的用户非常快速地构建和训练定制模型,这就是为什么 TensorFlow 团队考虑到我们的需求,致力于将 tf.keras 作为默认的高级 API。
TensorFlow Hub:转移学习爱好者的工具
如果在日常工作中,你从事各种与计算机视觉相关的应用,并且希望在这方面取得更快的进展,你或多或少会使用迁移学习。
(对于非人工智能观众)嗯,迁移学习只不过是一种方法,你可以从公开可用的数据集( MS-COCO , ImageNet )下载开源权重,这是其他人花了很多周、几个月才弄明白的,并将其用作对你自己的神经网络的非常好的初始化【而不是随机初始化】,以将这些大型公共数据集的知识转移到你自己的问题中。
截图来自 TensorFlow 网站(CC BY 4.0)
因此,现在根据最近的公告,在 TF Hub 中有超过 1000 个模型,文档、代码片段、演示和交互式 Google Colab 都在等待使用。
TensorFlow Extended (TFX):一个端到端的 ML 平台
截图自开发者峰会 2020 YouTube 视频[( CC BY 4.0)]
根据最近的更新,一旦您准备好将您的模型投入生产。 TensorFlow Extended 现在将允许您为您的可扩展、高性能机器学习任务构建生产就绪的[端到端] ML 管道。
TensorFlow 2.1 支持云 TPU
截图自开发者峰会 2020 YouTube 视频[( CC BY 4.0)]
这次更新是特别为 TPU 的情人们准备的。现在,你可以在专门为人工智能工作流设计的定制硬件上训练和部署你的模型和管道,比如 TPU 的。在最新版本中,TensorFlow 已经使用 Keras 针对云 TPU 进行了优化,这意味着您开始使用的相同 API 将帮助您扩展到 TPU 计算的千万亿次浮点运算,这将足以转变您的业务或创造下一个研究突破。
结束了
好了,伙计们,现在我们已经到了这篇文章的结尾。我知道,通过这篇文章,我只能对 TensorFlow Dev Summit 2020 上发布的公告和更新略知一二。
因此,请访问此链接观看 TensorFlow Dev Summit 2020 的所有会议。
感谢您的关注
你利用你的时间阅读我的《T21》作品对我来说意味着整个世界。我完全是这个意思。
此外,如果你愿意,可以在 Medium、LinkedIn 或 Twitter 上关注我!我很乐意。
阅读纳文·曼瓦尼在媒介上的作品。一个机器学习工程师,一个深度学习爱好者|谷歌印度…
medium.com](https://medium.com/@naveenmanwani) [## Naveen Manwani 机器学习工程师 AIMonk Labs Private Ltd | LinkedIn
查看纳文·曼瓦尼在全球最大的职业社区 LinkedIn 上的个人资料。Naveen 有一份工作列在他们的…
www.linkedin.com](https://www.linkedin.com/in/naveen-manwani-65491678/) [## 纳文·曼瓦尼
纳文·曼瓦尼的最新推文(@纳文·曼瓦尼 17)。机器学习工程师@ AIMONK Labs Pvt ltd,深…
twitter.com](https://twitter.com/NaveenManwani17)
云上的 Tensorflow 扩展、ML 元数据和 Apache Beam
一个使用 GCP 数据流的实用且独立的示例
tensorflow extended 通过运行tfx template copy taxi $target-dir
提供的完整端到端示例产生了分散在 5 个目录中的 17 个文件。如果你正在寻找一个更小、更简单、自包含的例子,它实际上运行在云上,而不是本地,这就是你正在寻找的。这里还提到了云服务的设置。
图片来自 pexels.com
会涵盖哪些内容
我们将为 Chicago taxi trips csv 数据集生成统计数据和模式,您可以通过运行data
目录下的tfx template copy taxi
命令找到该数据集。
通过连接到 ML 元数据存储,或者只是从简单的文件/二进制存储中下载工件,可以从 jupyter 笔记本中查看生成的工件,比如数据统计或模式。
文章底部的完整代码示例
使用的服务
- 数据流作为 Apache Beam 管道运行服务
- 存储桶作为简单(但快速)的二进制和文件存储服务
- (可选,但回报递减)云 SQL (MySQL)作为 ML 元数据的后备存储服务
整个管道可以在您的本地机器上运行(或者在不同的云提供商/您的定制 spark 集群上运行)。这是一个可以通过使用更大的数据集来扩展的例子。如果你希望明白这是如何透明地发生的,阅读这篇文章。
执行过程
- 如果在本地运行,代码将不会被序列化或发送到云中(当然)。否则,Beam 将把所有东西发送到一个中转位置(通常是铲斗存储)。查看 cloudpickle,直观了解电子监管是如何完成的。
- 您选择的云运行服务(我们的是数据流)将检查所有提到的资源是否存在并且可访问(例如,管道输出、临时文件存储等)
- 计算实例将被启动,您的管道将在分布式场景中执行,当它仍在运行或完成时,它将显示在作业检查器中。
将
/temp
或/tmp
用于临时文件,将/staging
或/binaries
用于暂存目录是一个很好的命名实践。
TFX 管道
Tensorflow Extended 提供了围绕普通旧梁组件的自定义组件包装器。它们在形式上更加联合:工件只是被生产和消费。这意味着它们不会每次都传输所有的数据集,它们只是传递资源定位器字符串。由于分析预处理速度的原因,您的数据集被流式传输,然后以小块的形式保存为tfrecords
,以获得最佳性能,充分利用存储桶的快速存储技术。
这就是为什么当你声明定制组件时,你声明了强类型输入和输出通道(工件类型和名称),它们被映射到光束侧的多个标记输入-输出。你用一个Dict
返回这些。请随意查看默认 TFX 组件的源代码,以获得关于这些组件的更多信息
这就是为什么你需要做这样的事情:
example_gen = CsvExampleGen(...)
statistics_gen = StatisticsGen(**examples=example_gen.outputs['examples']**)
你需要了解的另一个 TFX 抽象是 tfx.orchestration.pipeline。通过提供一组组件来形成管道 DAG,管道根数据源(文件系统或桶)和一个可选的元数据存储库(T17),您将控制权移交给 TFX 的BeamDagRunner
。所支持的射束管道执行参数是平台不可知的。以下是数据流的,供参考。
对于它所做的所有事情,我们的管道创建是非常小的
您可以使用带有通配符的模式一次读取多个文件。
这里是我们的数据流执行参数:
云服务设置
我们不需要太多就能运行。只是一个存储桶,要启用的数据流服务,如果你选择使用 ml 元数据,一个云 sql 实例。
- 登录谷歌云控制台
- 创建一个存储桶
- 启用数据流 API
- [可选] 创建一个云 SQL MySQL 实例用于 ML 元数据
- 创建服务帐户,应用正确的权限(云存储桶项目创建、删除、sql 访问)。
不要用自己的个人账号进行云操作。将一些凭证密钥导出为.json
文件,以便与GOOGLE_APPLICATION_CREDENTIALS
环境变量一起使用,这样您就有足够的权限将管道部署到数据流。 确保您的服务帐户 拥有足够的角色 来创建新的数据流作业,对云 SQL 实例进行读写,对桶存储进行读写。
6.确保服务可以相互通信(通常在第一次运行管道时进行故障诊断)
如果您的管道使用的不止是 tensorflow 扩展组件,请在
[setup.py](https://beam.apache.org/documentation/sdks/python-pipeline-dependencies/)
文件下包含设置运行环境所需的任何内容。更多信息请点击这里。
在云上运行管道并查看结果
你可能会得到
UserWarning: RuntimeParameter is only supported on KubeflowDagRunner currently. warnings.warn(‘RuntimeParameter is only supported on KubeflowDagRunner ‘
。现在可以随意忽略这个警告。只有参数化的流水线(模板)才可以用不同的配置运行。您通常将它们存放在存储服务中,并通过 post http 请求作业提交来运行它们。点击此处了解更多信息。
我们用BeamDagRunner().run(<your pipeline object here>)
。
*tfx.orchestration*
下有不同的滑道。这一个是从* *tfx.orchestration.beam.beam_dag_runner*
下的。*
图表时间
你可以看到我们的工作是在云控制台上完成的。在这里,您可以查看指标、日志消息、运行时间、每个工作者的 cpu 利用率。
吞吐量
cpu 利用率
您甚至可以查看不同的自动生成的统计数据(不是以 protobuf 格式保存的数据集统计数据)。
工作统计
生成的工件
让我们进入存储浏览器,检查我们的存储区。
存储桶统计
这些目录用于存放组件之间的二进制文件和数据,临时文件和暂存文件的临时存储。CsvExampleGen 包含直接从逗号分隔值文件生成的 tfrecords。StatisticsGen 和 SchemaGen 只包含一个(或者 2 个,如果您还指定了一个测试集)tfrecord 文件,该文件包含提取的统计数据和生成的模式。我们稍后会用笔记本检查那些。
您可以看到,除了对应于时间线中特定步骤的不同编号的文件夹之外,tfrecords 也是为了提高性能而拆分的。
旁注:这并不适用于非常非常大(TB 级)的数据集,因为这样一来,它们会成倍增加,并在桶中占据大量空间。在这些情况下,使用基于波束生成器的数据集会更有效,这超出了本教程的范围。
如果您使用 ML 元数据服务,没有理由在您的桶中手动查找这些。
人工制品检查
Tensorflow Extended 已经提供了可视化这些默认工件的方法。首先,模式是基于文本的,所以你可以直接打印出来。通过加载扩展,统计数据可以很容易地在 jupyter 笔记本中可视化。
这些数据存储在一个云原生环境中,手动检查这些数据有点棘手,但也很重要。对于此任务,您可以:
- 在 Google Colab 中进行身份验证,并从那里加载文件。(如果您不是项目的所有者或编辑,可能会失败,因为您不应该是所有者或编辑。仅使用服务帐户以获得良好的安全性)
- 启动一个微小的(可能可抢占的)虚拟机,并从那里启动一个笔记本实例(在数据流左侧面板中有一个快捷方式)
- 在本地手动导出数据,并使用本地 jupyter 实例检查它们。(这一点也不方便——最终是一种解决方法)
无论您选择做什么,或者如果您的数据是基于文件还是 ml 元数据形式,我们都可以满足您的需求。
访问普通文件很容易。如果您在一个计算实例/虚拟机/笔记本中,它是存储桶所在的 GCP 项目的一部分,那么您只需使用gs://<bucket name>
url 来访问文件,就像访问您的文件系统一样。
- 对于这个模式,它非常简单。这是一个小的协议缓冲区,以文本格式保存。如果您了解模式协议缓冲区的定义,您可以在文本编辑器中检查或更改它。
或者,正确且类型安全的方法是用 python 加载并编辑它。将返回一个类型保存协议缓冲区存根,您可以使用它进行编辑。
同样,对于统计协议缓冲区(这些不是文本可读的)。
结论
现在,您应该能够充分理解 tensorflow 扩展生态系统所提供的功能,以及它的组件如何与 cloud beam runner 和其他公共云服务协作工作。
谢谢你一路看完!
下面是完整的代码示例:
面向完全初学者的 Tensorflow:张量入门
对“张量”及其运算的理解,可以帮助完全初学者从零开始。
图片来自 Pixabay (免费使用)
在这个令人兴奋的人工智能(AI)世界,Tensorflow 如今是一个热门词汇,尤其是在深度学习继续快速加速人工智能进展的情况下。但是对于刚开始使用 Tensorflow 的人来说,这种体验可能会令人害怕,因为漂亮的库的术语和用法可能会让完全的初学者感到困惑。当我刚开始学习 Tensorflow 时,我面临着类似的挑战,希望通过这篇文章简化一些错综复杂的事情。本文需要对 Python 有一个基本的了解,才能对 Tensorflow 有一个更清晰的了解。
张量流是如何工作的?
描述 Tensorflow 的基本(也是最简单的)方式是,它是 Python(可能还有其他编程语言)中一个很酷的库,允许我们创建计算图来开发神经网络模型。组成 Tensorflow 对象的基本元素是一个 张量 ,所有执行的计算都发生在这些张量中。所以从字面上看(用我的话说),当你开发任何神经网络模型时,这些张量以有序的方式流动,并在评估时产生最终输出。所以是的,了解张量到底是什么以及我们如何使用它们是开始使用 Tensorflow 的第一步。
那么什么是张量呢?
我们都很熟悉编程中的数据类型,对吗? 1、2、3 等。是整数,带小数点的数字 (1.5,3.141,5.2555 等。)是浮点值,另一个常见的是字符串(比如“Hello。你今天怎么样?”)。当我们把多个元素集合在一起时,我们一般称之为**(例如【1,4,7】)。
列表集合可用于开发如下矩阵:
[[1,4,7],[5,8,12],[1,66,88]]
现在,让我们来看看张量——它们基本上是常规矩阵和向量的高维表示/概括。它甚至可以是 1 维矩阵、2 维矩阵、4 维矩阵或 n 维矩阵!所以我们可以有多维数组,而不仅仅是张量中的一个列表。就是这样,这就是张量在最基本的意义上的真正含义。就像我们可以在传统编程中给变量赋值一样,我们也可以在 Tensorflow 中这样做。只是有一种专门的方法来做这件事。类似地,就像你可以在编程中对常规变量(或常数)执行多重操作一样,我们也可以用张量来做。我们可以将张量切片并选择一部分元素,张量有不同的数据类型(整数、浮点、字符串等。)等等。
开始使用 Python 中的 tensor flow
第一步是安装漂亮的库!匹普是你在这里需要的一切。请注意,如果您使用的是 Google Colab(与 Python 中的 Jupyter notebooks 非常相似),您甚至不需要安装这个库,因为它已经安装好了,可以随时使用。谢谢你,谷歌!
*#Install the tensorflow module (not required in Google Colab, but is needed in your local PC)!pip install tensorflow*
当我们在本文中使用 Colab 时,输出中显示了消息“需求已经满足”。
需求已经满足:tensor flow in/usr/local/lib/python 3.6/dist-packages(2 . 3 . 0)
Tensorflow 目前有一个更老的版本 type 1.x,而最新发布的版本使用的是 2.x,所以我们选择当前的 tensorflow 版本。
*#Here, we would go with the latest (2.x) release of tensorflow by selecting the version#Note: By default, Colab tends to use the 2.x version%tensorflow_version 2.x*
让我们使用黄金关键词 import 来导入 tensorflow 模块!是的,你可以通过简单地打印版本属性来检查你正在使用的库的版本。
*#import the tensorflow moduleimport tensorflow as tf#Display the tensorflow version we are usingprint(tf.version)*
现在,让我们开始创建张量。我们将在这里为 int,float 和 string 创建一些。注意使用 tf 创建张量的特殊方式。变量属性。这意味着我们正在创建一个具有可变性质的张量,因此我们可以通过执行专门的操作来改变/修改它的值,就像我们在使用变量的常规编程中所做的那样。请注意,虽然我们在传统编程中已经使用了 int、float 和 string 来声明这些数据类型的相应变量,但我们将在 Tensorflow 中使用 tf.int16 (这意味着我们正在定义一个 16 位整数)、 tf.float32 (定义一个 32 位浮点值)和 tf.string 来这样做。注意,我们也可以使用 tf.float16、tf.int32 等等,这取决于我们想要存储的值的需求。如果你曾经使用过 C++,你应该知道像 int,short int,long long int,float,double 等。用于声明位数更少(或更多)的变量,所以我们在 Tensorflow 中做一些类似的事情。
*#The science (and art) of creating tensorsscalar_val = tf.Variable(123,tf.int16)floating_val = tf.Variable(123.456,tf.float32)string_val = tf.Variable(“hello everyone. Nice to learn tensorflow!”,tf.string)*
而现在,非常容易打印出这些张量的值!
*#Let us display the values (print) these tensorsprint(scalar_val)print(floating_val)print(string_val)*
<tf.variable shape="()" dtype="int32," numpy="123"></tf.variable>
<tf.variable shape="()" dtype="float32," numpy="123.456"><tf.variable shape="()" dtype="string," numpy="b’hello" everyone.="" nice="" to="" learn="" tensorflow=""></tf.variable></tf.variable>
我们可以确定张量的形状和等级,就像你可能在学校的数学模块中听到的那样。如果不是,形状只是指张量的每个维度所包含的元素的数量。而秩是张量中嵌套的最深层次。进入一些代码可能会更清楚。
*#The idea behind shape and rank of tensors#Shape: Describes the dimension of the tensor (total elements contained along each dimension)scalar_val_shap = tf.shape(scalar_val)print(scalar_val_shap)floating_val_shap = tf.shape(floating_val)print(floating_val_shap)*
tf。张量([],shape=(0,),dtype=int32)
tf。张量([],shape=(0,),dtype=int32)
所以我们得到 0 的形状,对吗?这只是意味着这些是标量值,而不是列表或嵌套列表。
*#Now, if we use e.g. lists/nested lists instead of just a “single” scalar valuelist_tensor1 = tf.Variable([1,3,5,6],tf.int16)print(list_tensor1)print(tf.shape(list_tensor1))list_tensor2 = tf.Variable([[1,2,3],[4,5,6]],tf.int16)print(list_tensor2)print(tf.shape(list_tensor2))#how about the rank? It describes the level of nesting within the tensor in simple words.print(tf.rank(list_tensor1))print(tf.rank(list_tensor2))*
你可以看到 list_tensor1 有 4 个元素,所以它的形状是(4,)。注意,( 4,)只不过是(4,1 ),只显示了单个列表中的 4 个元素。接下来,对于 list_tensor2 ,观察(2,3)的形状,表示我们有一个嵌套列表——有 2 个列表,每个列表包含 3 个元素。
<tf.variable shape="(4,)" dtype="int32," numpy="array([1,"></tf.variable>
tf。张量([4],shape=(1,),dtype=int32)
<tf.variable shape="(2," dtype="int32," numpy="array([[1,"></tf.variable>
tf。张量([2 ^ 3],shape=(2,),dtype=int32)
tf。张量(1,shape=(),dtype=int32)
tf。张量(2,shape=(),dtype=int32)
类似地,如果你看一下秩,你会看到, list_tensor1,的秩为 1,因为我们只有一个列表,这里没有嵌套!现在,对于 list_tensor2,你可以看到等级为 2,表示我们有一个 2 级嵌套,即一个大列表包含更多的列表。如果它是一个大列表,包含另一个小列表,而那个小列表包含更多更小的列表,我们的排名就会是 3。希望这能让你明白。
tf。张量(1,shape=(),dtype=int32)
tf。张量(2,shape=(),dtype=int32)
我们可以使用 tf.reshape. 来改变张量的形状(当然,直到它在数学上有效为止)。同样,请注意使用 Python 来做这件事的特殊方式。
*#Reshaping tensorsreshaped_list_tensor2 = tf.reshape(list_tensor2,[6])print(reshaped_list_tensor2)list_tensor3 = tf.Variable([[1,2,3,1],[1,9,10,11],[1,5,11,22],[16,17,18,19]],tf.int16)print(list_tensor3)print(tf.rank(list_tensor3))print(tf.shape(list_tensor3))reshaped_list_tensor3 = tf.reshape(list_tensor3,[2,8,1])print(reshaped_list_tensor3)#or like thisreshaped_list_tensor3 = tf.reshape(list_tensor3,[8,2,1])print(reshaped_list_tensor3)#or automatically determine the shape by only giving one dimension!reshaped_list_tensor3 = tf.reshape(list_tensor3,[1,-1])print(reshaped_list_tensor3)*
你会得到下面的输出。它太长了,可能无法阅读,但我猜如果你在这里查看 Colab 笔记本会更清楚。这里发生的基本事情是,如果你在这里为形状指定[6],tensorflow 将把你的张量重新整形为具有 6 个一维元素(因此它将是一个简单的 1D 列表)。如果你做一个[2,8,1],它会产生 2 个列表,每个列表有 8 个元素。你也可以做[8,2,1]-创建 8 个列表,每个列表有 2 个元素,就像你看到的那样。如果您不想亲自指定整形的整个尺寸,您也可以简单地使用-1。因此,[1,-1]的整形将整形 list_tensor3 以创建reshaved _ list _ tensor 3,那么 1 list 与???是的,16 种元素。因此,请注意,尽管您可以按照自己的意愿使用 reshape,但必须确保 reshape 后的元素总数在整个过程中不会发生变化。
tf。张量([1 2 3 4 5 6],shape=(6,),dtype=int32)
<tf.variable shape="(4," dtype="int32," numpy="array([["></tf.variable>
tf。张量(2,shape=(),dtype=int32)
tf。张量([4 ^ 4],shape=(2,),dtype=int32)
tf。张量([[1][2][3][1][1][9][10][11]][[1][5][11][22][16][17][18][19]]),shape=(2,8,1),dtype=int32)
tf。张量([[[1][2]][[3][1]][[1][9]][[10][11]][[1][5]][[11][22]][[16][17]][[18][19]]),shape=(8,2,1),dtype=int32)
tf。张量([[ 1 2 3 1 1 9 10 11 1 5 11 22 16 17 18 19]],shape=(1,16),dtype=int32)
你可以使用特殊命令 tf.ones 或 tf.zeros. 创建一个全是 1(或 0)的张量,然后也可以对它们执行类似的操作。
现在,我们来切片。就像你可以从一个矩阵或者一个 Python 列表中切分和提取一些元素一样,你也可以对张量做同样的事情。请看下面的例子,其中,我们首先创建一个全张量(我的名字是全 1 张量)和另一个全 0 张量。您想要创建的张量的维数在方括号[]内,因此 a [4,4,4,1]创建了一个 4D 张量,而 a [5,5]创建了一个 2D 张量——在最简单的意义上,它有 5 行 5 列(不过是一个矩阵)。
下面显示了这些创建的张量的一些切片操作。这样你就可以从张量中得到你喜欢的元素而不考虑你不喜欢的:-)
*#creating a tensor full of 1s (or 0s)tensor_onefull = tf.ones([4,4,4,1])print(tensor_onefull)tensor_zerofull = tf.zeros([5,5])print(tensor_zerofull)#extracting specific values from tensors (similar to slicing in conventional programming)tensor_sliced_onefull = tensor_onefull[0]print(tensor_sliced_onefull)tensor_sliced_zerofull = tensor_zerofull[0,1]print(tensor_sliced_zerofull)*
这是很好的输出(在 Colab 笔记本中可读性更好)。
tf。张量([[[[1。] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]]] [[[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]]] [[[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]]] [[[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]]]],shape=(4,4,4,1),dtype=float32)
tf。张量([[0。0.0.0.0.] [0.0.0.0.0.] [0.0.0.0.0.] [0.0.0.0.0.] [0.0.0.0.0.]],shape=(5,5),dtype=float32)
tf。张量([[[1。] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]] [[1.] [1.] [1.] [1.]]],shape=(4,4,1),dtype=float32)
tf。张量(0.0,shape=(),dtype=float32)
*#another example from previously created tensorprint(list_tensor3)tf_slicedexampleagain = list_tensor3[0,-2:]print(tf_slicedexampleagain)#selecting multiple rowstf_slicedexampleagain = list_tensor3[1::]print(tf_slicedexampleagain)*
同样,一些很酷的输出(基本但没错,最简单的东西是最酷的)。
<tf.variable shape="(4," dtype="int32," numpy="array([["></tf.variable>
tf。张量([3 1],shape=(2,),dtype=int32)
tf。张量([[ 1 9 10 11] [ 1 5 11 22] [16 17 18 19]],shape=(3,4),dtype=int32)
你可以在我的 Githubhttps://Github . com/joyjitchatterjee/deep learning-tensor flow-Basics/blob/master/tensor flow _ beginning . ipynb上找到这篇文章的完整的 Colab 笔记本。
仅此而已。希望你喜欢阅读这篇文章。感谢阅读,并为任何错别字/错误道歉。我希望在我的下一篇文章中涉及更多关于 Tensorflow 的内容。
如果你愿意,你可以通过 LinkedIn 联系我,电话是http://linkedin.com/in/joyjitchatterjee/
参考文献
- 张量流文档(https://www.tensorflow.org/guide)
- TensorFlow 2.0 完整教程— Python 神经网络初学者教程(https://www.youtube.com/watch?v=tPYj3fFJGjk&list = ply lya 78 q 4 izualaaoer 3 qwuzkqltzf 7 qk)
- TensorFlow 和深度学习入门(https://medium . com/@ rishit . dagli/get-started-with-tensor flow-and-Deep-Learning-part-1-72c 7d 67 f 99 fc)
网飞电影推荐—使用深度学习
网飞使用深度学习
使用 TensorFlow 构建电影推荐模型
在 Unsplash 上由 Myke Simon 拍摄的照片
在这个由两部分组成的系列中,我们将了解如何使用 TensorFlow 构建电影推荐模型。
medium.com](https://medium.com/analytics-vidhya/tensorflow-for-recommendation-model-part-1-19f6b6dc207d)
让我们从停止的地方继续。在之前的系列文章中,我们使用矩阵分解来学习用户嵌入向量,从而为我们执行推荐。这些用户嵌入表示用户和被称为反馈矩阵的电影矩阵之间的学习特征。现在,让我们了解如何使用深度学习来执行建议。下面附上了谷歌实验室。
第二部分神经 n。
colab.research.google.com](https://colab.research.google.com/drive/11zEddP5TUAVCTkUgFwtFN7LG1z2A85hB#scrollTo=TOF0qSte0ljl&uniqifier=4)
矩阵分解的缺点
在很大程度上,使用 MF 来执行推荐有一些缺点:
- 小特征空间:电影或者用户 id 唯一可以查询的特征。
- 推荐的相关性:由于 MF 讲的是用点积或者欧氏距离,所以把最受欢迎的项目推荐给大家。
卷积神经网络
在神经网络中,CNN 仍然是执行图像分类、图像识别、目标检测等的主要算法。
在神经网络中,卷积神经网络(ConvNets 或 CNN)是进行图像处理的主要类别之一
medium.com](https://medium.com/@RaghavPrabhu/understanding-of-convolutional-neural-network-cnn-deep-learning-99760835f148)
然而,在建议模型中,我们可以着手创建一个简单的 softmax CNN 模型,其中的参数为:
- 输入:用户查询输入和与之相关的条目。
- 输出:用户与物品的交互概率。
在输入空间中,可以添加更多的辅助功能,如手表时间、国家等。这将允许隐藏层中的神经网络学习更多的数据,从而在这些层中建立更多的连接。
激活层
通过定义神经网络的结构,可以增加激活层来进一步捕获更多的隐藏连接,并在隐藏层中形成更好的特征采样。对于本推荐模型,将使用校正线性单位(ReLU)。
在大多数神经网络中,将使用梯度下降算法,其中在误差反向传播期间,需要激活函数作为线性函数。这就是 ReLU 的用武之地。
如果我们看一下左边的图像,我们可以看到函数的方程可以表示为 y=max(0,x),其中 表示当 x 的值大于 0 时采用 y 的值。这充当线性和非线性函数,其中该函数对于大于零的量是线性的,这意味着它具有线性激活函数的许多期望属性。然而,它是一个非线性函数,因为负值总是输出为零。这就是为什么 ReLu 激活功能大多用在现代 NN 上的原因。
Softmax 作为最后一层
该模型通过 softmax 层将最后一层的输出映射到概率分布。一种函数,将 X 个实数的向量作为输入,并将其归一化为由 X 个概率组成的概率分布,这些概率与输入数的指数成比例。从建议模型的角度来看:
- x 代表用户-特征交互输出的个体概率。这可以是用户接下来将要观看的建议电影
- ∑(x)表示该输出在最终层中所有输出上分布的概率
学机器学习?具体来说,尝试深度学习的神经网络?您可能遇到了…
medium.com](https://medium.com/data-science-bootcamp/understand-the-softmax-function-in-minutes-f3a59641e86d)
神经网络中的嵌入
与 MF 模型相比,NN 模型也具有嵌入来表示用户和项目之间的关系。模型学习嵌入,每个项目的 V,j。
然而,查询嵌入是不同的。系统确定从查询特征到嵌入的映射,而不是每个查询学习一个嵌入。因此,由于激活函数(ReLU)的存在,与 MF 模型相比,嵌入现在被概括为非线性函数。
有可能对这些嵌入执行点积来获得最终的概率分数,但是这不是本系列的范围的一部分。
输入数据
现在让我们为创建的 NN 模型定义训练数据。训练数据由查询特征和用户与之交互的项目向量(表示为概率分布)组成。变量或权重表示用户项目特征的相互作用,并且被随机初始化用于训练。
创建数据
输入数据是从 ratings()数据帧和 movies()数据帧创建的。这两个 DF 代表我们必须整理以创建输入数据的基本信息:
List comprehension 在某种程度上被用来创建这个特性,因为它速度更快,而且当内存成为一个问题时,它也是大多数程序员现在使用的东西。
数据批次
至于训练输入数据,使用批处理将数据传播到 softmax 模型中。一个批次的大小是在模型被更新之前处理的样本的数量,当地面真实值和输出被比较用于另一个迭代时。一批 4 个特征将被传播到 NN 模型中。这将是电影 id、年份、类型和标签。因此,每个用户将在神经网络模型中学习这些特定的交互。TF . data . dataset()用于初始化批量操作。批量大小至关重要,因为较大的批量大小(有内存限制)将允许神经网络在优化阶段更有效地学习
神经网络训练
一旦输入数据和批次被处理,神经网络训练最终可以开始。创建嵌入()函数来捕获训练数据的特征。softmax 最终层神经网络模型用于输出由 K 个概率组成的概率分布,这些概率与输入数的指数成比例。
最终总结
客观地说,矩阵因式分解经常用在大型稀疏数据环境中,其中可以使用简单点积函数来识别与用户最接近的项目。这在系统中时间和内存受限的情况下特别有用。它更容易扩展,查询更便宜,速度更快。
至于神经网络,可以形成更复杂的关系来理解推荐在模型内部如何工作。神经网络模型捕捉更个性化的偏好,因为输入可以是用户与国家、年龄、观看时间等交互的向量。在某些情况下,神经网络模型更受青睐,因为预测将更加准确,更加面向用户/有偏向,而不是纯粹基于用户和项目之间的点距离。这对于其他用例特别有用,如市场营销或广告定位,其中收集了大量用户数据,NN 可以了解每个用户的潜在偏好。同样通过使用 NN,查询嵌入通常需要在查询时计算;结果,这在计算上可能是昂贵的。
最后
如果您成功地保持了对这一点的关注,如果您对本系列有任何建议,或者有任何尝试使用 NN 的 MF 构建推荐模型的经验,请留下评论。*
普雷姆·库马尔 是一个无私的学习者,对围绕我们的日常数据充满热情。在 LinkedIn 上与我联系当你写信谈论这个故事以及等待的未来发展时,请提及这个故事。
网飞电影推荐—使用协同过滤
我们将了解如何使用 TensorFlow 构建一个电影推荐模型。
什么是推荐模型?
推荐模型,简而言之,是一种算法,旨在根据用户的行为向用户提供最相关和最相关的信息。像网飞和谷歌这样的公司有一个收集数据行为的巨大数据库,能够执行最先进的推荐,以便他们可以向用户显示最相关的内容或服务,以增加参与度。在第 1 部分中,我们将构建一个推荐模型,使用 协同过滤 向用户推荐电影。
笔记本
colab.research.google.com](https://colab.research.google.com/drive/1GV4lg3LRN-ghtAwJbN_Xy9tQmpAEykPY)
协同过滤?
协同过滤处理用户和项目之间的相似性以执行推荐。这意味着算法不断地发现用户之间的关系,并依次进行推荐。算法学习用户之间的嵌入,而不必调整特征。最常见的技术是通过执行矩阵分解来找到构成特定用户兴趣的嵌入或特征。
矩阵分解
矩阵分解是一种嵌入。假设我们有一个用户电影矩阵或反馈矩阵,一个 ᴺᴹ,模型学习分解成:
- A 用户 嵌入向量 U ,其中第 N 行是项目 m 的嵌入
- An 项 嵌入向量 V ,其中 M 行是对项 N 的嵌入
学习嵌入向量,使得通过执行 UVᵀ,可以形成反馈矩阵的近似。
损失函数
为了逼近反馈矩阵,需要一个损失函数。直观的损失函数之一是使用均方误差(MSE)。MSE 计算反馈矩阵 a 和近似 UVᵀ矩阵的差值。简单来说:
其中:
- n 代表用户总数
- Yᵢ 代表反馈矩阵 a
- Yᵢ_bar 代表 UVᵀ的近似矩阵
正则化函数
训练模型最常见的问题之一是 过度拟合。如果这个特定的异常特征具有大的“幅度”或偏差,那么可以说该模型对于这些特定特征是过度拟合的。为了减少这种情况,我们可以在 MSE 损失中增加一个惩罚项。最常用的模型是套索回归和岭回归。
训练机器学习模型的一个主要方面是避免过度拟合。该模型将有一个低…
towardsdatascience.com](/regularization-in-machine-learning-76441ddcf99a)
岭回归(L2)
岭公式
- 取系数的平方、T43、w、。因此,任何大的 w 值都将被扣分,以减少过度拟合。
拉索回归(L1)
套索公式
- 取系数的大小,, w ,。因此,任何大的 w 值都会被扣分,以减少过度拟合。
- 如果你有一个稀疏矩阵,收缩系数和执行特征选择是有用的,这正是我们所需要的。
提出建议
一般来说,步骤(和功能)如下:
- 创建一个稀疏张量 : tf。SparseTensor() ,用于随机初始化的和 V 矩阵
- 创建损失函数和优化器:TF . losses . mean _ squared _ error(),来估计带有正则化惩罚和 SGD 作为优化器的总损失
- 创建模型 : tf。Session() ,初始化超参数、学习率和嵌入
- 列车车型: tf。Session.run(),学习反馈矩阵的嵌入并返回 v 和 k 作为嵌入向量
- 展示推荐: df。DataFrame(),显示与被查询用户最近的电影
准备好模型后,让我们尝试计算用户 ID: 500 的推荐值
决赛成绩
我们可以看到,该模型已经学习了用户 500 的嵌入,并且它推荐了用户 ID: 500 可能喜欢的前 5 部电影。
最后
如果您成功地保持了对这一点的关注,如果您对本系列有任何建议,或者有任何尝试使用 NN 的 MF 构建推荐模型的经验,请留下评论。
普雷姆·库马尔 是一个无私的学习者,对我们身边的日常数据充满热情。在LinkedIn上与我联系,当你写信谈论这个故事和等待的未来发展时,提及这个故事。**
TensorFlow Lite Android 支持库:简化 Android 上的 ML
介绍新的 TF Lite Android 支持库
Benjamin Sow 在 Unsplash 上拍摄的照片
每个人都喜欢 TensorFlow,当你可以直接在 Android 上运行 TF 模型时就更喜欢了。我们都在 Android 上使用 TensorFlow Lite,我们也有几个 CodeLabs 在上面。使用 Android 上的Interpreter
类,我们目前正在应用程序中运行我们的.tflite
模型。
但是在那之前我们要做很多事情,对吗?如果我们正在执行图像分类任务,你可能会从相机库中获得一个Bitmap
或Image
对象,然后我们将其转换为float[][][]
或byte[]
。然后我们从assets
文件夹中加载我们的模型作为MappedByteBuffer
。在调用了interpreter.run()
之后,我们获得了类概率,我们对其执行argmax()
操作,然后最终从labels.txt
文件中获得一个标签。
这是我们开发人员遵循的传统方法,没有其他方法。
TensorFlow Lite Android 支持库简介
TensorFlow 团队发布了 TensorFlow Lite Android 支持库来解决预处理的繁琐任务。GitHub 页面给出了他们目标的直觉,
移动应用程序开发人员通常与位图等类型化对象或整数等原语进行交互。然而,运行设备上机器学习模型的 TensorFlow Lite 解释器使用 ByteBuffer 形式的张量,这可能很难调试和操作。TensorFlow Lite Android 支持库旨在帮助处理 TensorFlow Lite 模型的输入和输出,并使 TensorFlow Lite 解释器更易于使用。
首先,我们需要在我们的 Android 项目中做到这一点。还记得build.gradle
文件吗?对!我们将把这些依赖关系添加到我们的应用程序级build.gradle
文件中,
1.创建输入和输出数组
运行 TFLite 模型的第一步是创建一些数组对象,它可以存储模型的输入以及模型将产生的输出。为了让我们的生活更轻松,减少与float[]
对象的斗争,TF 支持库包含了一个[TensorBuffer](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/experimental/support/java/src/java/org/tensorflow/lite/support/tensorbuffer/TensorBuffer.java)
类,它采用所需数组的形状及其数据类型。
片段 1
注:截至 2020 年 4 月 1 日,仅支持
**DataType.FLOAT32**
和**DataType.UINT8**
。
您甚至可以通过修改现有的TensorBuffer
对象的数据类型来创建一个TensorBuffer
对象,
**val** newImage = TensorImage.createFrom( image , DataType.**FLOAT32** )
2.图像操作
如果您正在处理对象检测、图像分类或其他与图像相关的模型,您需要在Bitmap
上工作,并调整其大小或使其正常化。我们对此有三个操作,即[ResizeOp](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/experimental/support/java/src/java/org/tensorflow/lite/support/image/ops/ResizeOp.java)
、[ResizeWithCropOrPadOp](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/experimental/support/java/src/java/org/tensorflow/lite/support/image/ops/ResizeWithCropOrPadOp.java)
和[Rot900p](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/experimental/support/java/src/java/org/tensorflow/lite/support/image/ops/Rot90Op.java)
。
首先,我们使用[ImageProcessor](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/experimental/support/java/src/java/org/tensorflow/lite/support/image/ImageProcessor.java)
类定义预处理管道。
问题:什么是双线性和最近邻法?
答:读这个。
接下来,创建一个[TensorImage](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/experimental/support/java/src/java/org/tensorflow/lite/support/image/TensorImage.java)
对象并处理图像。
3.张量运算
图像阵列的归一化对于几乎所有的模型都是必要的,无论是图像分类模型还是回归模型。对于处理张量,我们有一个[TensorProcessor](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/experimental/support/java/src/java/org/tensorflow/lite/support/common/TensorProcessor.java)
。除了[NormalizeOp](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/experimental/support/java/src/java/org/tensorflow/lite/support/common/ops/NormalizeOp.java)
,我们还有[CastOp](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/experimental/support/java/src/java/org/tensorflow/lite/support/common/ops/CastOp.java)
、[QuantizeOp](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/experimental/support/java/src/java/org/tensorflow/lite/support/common/ops/QuantizeOp.java)
和[DequantizeOp](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/experimental/support/java/src/java/org/tensorflow/lite/support/common/ops/DequantizeOp.java)
。
问题:什么是正常化?
答:将实际值范围转换为标准值范围的过程,通常为-1 到+1 或 0 到 1。例如,假设某个特征的自然范围是 800 到 6000。通过减法和除法,您可以将这些值规范化到-1 到+1 的范围内。
此外,我们可以通过实现[TensorOperator](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/experimental/support/java/src/java/org/tensorflow/lite/support/common/TensorOperator.java)
类来自由构建定制的 ops,如下所示。
4.加载模型和标签
我们可以使用[FileUtil.loadMappedFile()](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/lite/experimental/support/java/src/java/org/tensorflow/lite/support/common/FileUtil.java)
方法轻松加载我们的.tflite
模型。类似地,我们可以从InputStream
或资产文件夹中加载标签。
然后使用Interpreter.run()
进行推理,
关于 Android 机器学习的更多资源
仅此而已!
我希望你喜欢新的 TensorFlow Lite Android 支持库。这是一个内部的快速审查,但尝试探索自己。感谢阅读!
TensorFlow 还是 PyTorch?
深度学习网络编程的最佳框架是什么?
UPC Barcelona Tech的一名学生问我为神经网络编程的最佳框架是什么?TensorFlow 还是 PyTorch?。我的回答是:别急,你从哪一个开始,选择哪一个都不重要,重要的是开始, 走吧!
在两种环境中对神经网络进行编程的步骤在机器学习中是相同的:
- 导入所需的库,
- 加载并预处理数据,
- 定义模型,
- 定义优化器和损失函数,
- 训练模型,最后
- 评估模型。
这些步骤可以在任一框架中非常相似地实现。为此,在本出版物中,我们将构建一个神经网络模型,该模型将在 API PyTorch 和 TensorFlow 的 API Keras 中对手写数字进行分类。整个代码可以在 GitHub 上测试和运行它作为一个谷歌笔记本。
1。神经网络编程步骤
a)导入所需的库
在这两个框架中,我们首先需要导入一些 Python 库,并定义一些训练所需的超参数:
import numpy as np
import matplotlib.pyplot as plt epochs = 10
batch_size=64
对于 TensorFlow,您只需要这个库:
import tensorflow as tf
而在 PyTorch 的例子中,这两个:
import torch
import torchvision
b)加载和预处理数据
使用 TensorFlow 加载和准备数据可以通过这两行代码完成:
(x_trainTF_, y_trainTF_), _ = tf.keras.datasets.mnist.load_data() x_trainTF = x_trainTF_.reshape(60000, 784).astype('float32')/255 y_trainTF = tf.keras.utils.to_categorical(y_trainTF_,
num_classes=10)
在 PyTorch 和另外两个人在一起的时候:
xy_trainPT = torchvision.datasets.MNIST(root='./data', train=True, download=True,transform=torchvision.transforms.Compose([torchvision.transforms.ToTensor()])) xy_trainPT_loader = torch.utils.data.DataLoader(xy_trainPT, batch_size=batch_size)
我们可以验证这两个代码已经用库matplotlib.pyplot
加载了相同的数据:
print("TensorFlow:")
fig = plt.figure(figsize=(25, 4))
for idx in np.arange(20):
ax = fig.add_subplot(2, 20/2, idx+1, xticks=[], yticks=[])
ax.imshow(x_trainTF_[idx], cmap=plt.cm.binary)
ax.set_title(str(y_trainTF_[idx]))
print("PyTorch:")
fig = plt.figure(figsize=(25, 4))
for idx in np.arange(20):
ax = fig.add_subplot(2, 20/2, idx+1, xticks=[], yticks=[])
ax.imshow(torch.squeeze(image, dim = 0).numpy(),
cmap=plt.cm.binary)
image, label = xy_trainPT [idx]
ax.set_title(str(label))
c)定义模型
为了定义模型,在这两种情况下,都使用了相当相似的语法。在 TensorFlow 的情况下,可以使用以下代码完成:
modelTF = tf.keras.Sequential([ tf.keras.layers.Dense(10,activation='sigmoid',input_shape=(784,)), tf.keras.layers.Dense(10,activation='softmax')
])
在 PyTorch 有这样一个:
modelPT= torch.nn.Sequential(
torch.nn.Linear(784,10),
torch.nn.Sigmoid(),
torch.nn.Linear(10,10),
torch.nn.LogSoftmax(dim=1)
)
d)定义优化器和损失函数
同样,指定优化器和损失函数的方式是完全相同的。使用 TensorFlow,我们可以这样做:
modelTF.compile(
loss="categorical_crossentropy",
optimizer=tf.optimizers.SGD(lr=0.01),
metrics = ['accuracy']
)
而 PyTorch 是这样的:
criterion = torch.nn.NLLLoss()
optimizer = torch.optim.SGD(modelPT.parameters(), lr=0.01)
e)训练模型
谈到培训,我们发现最大的差异。在 TensorFlow 的例子中,我们可以只用这一行代码来完成:
_ = modelTF.fit(x_trainTF, y_trainTF, epochs=epochs,
batch_size=batch_size, verbose = 0)
而在 Pytorch,我们需要像这样更长的东西:
for e in range(epochs):
for images, labels in xy_trainPT_loader:
images = images.view(images.shape[0], -1)
loss = criterion(modelPT(images), labels)
loss.backward()
optimizer.step()
optimizer.zero_grad()
在 PyTorch 中,没有像 Keras 或 Scikit-learn 中的fit()
那样的“预制”数据模型调优函数,所以训练循环必须由程序员指定。嗯,在简单性和实用性之间,这里有某种妥协,以便能够做更多定制的事情。
f)评估模型
当我们需要评估模型时,也会发生同样的情况,而在 TensorFlow 中,您只需用测试数据调用方法evaluate()
:
_, (x_testTF, y_testTF)= tf.keras.datasets.mnist.load_data()
x_testTF = x_testTF.reshape(10000, 784).astype('float32')/255
y_testTF = tf.keras.utils.to_categorical(y_testTF, num_classes=10)
_ , test_accTF = modelTF.evaluate(x_testTF, y_testTF)
print('\nAccuracy del model amb TensorFlow =', test_accTF)TensorFlow model Accuracy = 0.8658999800682068
在 PyTorch 中,再次要求程序员指定评估循环:
xy_testPT = torchvision.datasets.MNIST(root='./data', train=False, download=True,
transform=torchvision.transforms.Compose([torchvision.transforms.ToTensor()]))
xy_test_loaderPT = torch.utils.data.DataLoader(xy_testPT)
correct_count, all_count = 0, 0
for images,labels in xy_test_loaderPT:
for i in range(len(labels)):
img = images[i].view(1, 784)
logps = modelPT(img)
ps = torch.exp(logps)
probab = list(ps.detach().numpy()[0])
pred_label = probab.index(max(probab))
true_label = labels.numpy()[i]
if(true_label == pred_label):
correct_count += 1
all_count += 1
print("\nAccuracy del model amb PyTorch =", (correct_count/all_count))TensorFlow model Accuracy = 0.8657
2。更重要的是他们将会融合!
正如这个简单的例子所示,在 TensorFlow 和 PyTorch 中创建神经网络的方式并没有什么不同,除了程序员实现训练和评估循环的一些细节,以及一些超参数如epochs
或batch_size
在不同的步骤中指定。
事实上,这两个框架在过去的两年中一直在不断融合,相互学习并采用各自的最佳特性。例如,在几周前发布的 TensorFlow 2.2 的新版本中,训练步骤可以像 PyTorch 一样完成,现在程序员可以通过实现traint_step()
来指定循环体的详细内容。所以不要担心选择“错误”的框架,它们会收敛的!最重要的是学习背后的深度学习理念,你在其中一个框架里获得的所有知识,在另一个框架里都会对你有用。
3。产还是研?
然而,很明显,如果你想要的是将解决方案投入生产或在神经网络中进行研究,这是不同的。在这种情况下,选择哪一个的决定很重要。
TensorFlow 是一个非常强大和成熟的 Python 库,具有强大的可视化特性和各种用于高性能模型开发的选项。它具有为生产做好准备的展示选项,以及对 web 和移动平台的自动支持。
另一方面,PyTorch 仍然是一个年轻的框架,但是有一个非常活跃的社区,尤其是在研究领域。门户梯度 如图附图py torch 的兴起和采用研究社区基于主要会议主题(CVPR、ICRL、ICML、NIPS、ACL、ICCV 等)发表的研究论文数量。).
从图中可以看出,2018 年 PyTorch 框架的使用是少数,相比之下,2019 年研究人员对它的使用是压倒性的。所以如果想打造人工智能相关的产品,TensorFlow 是个不错的选择。如果你想做研究,我推荐 PyTorch。
因此,如果你想打造与人工智能相关的产品,TensorFlow 是一个不错的选择。如果你想做研究,我推荐 PyTorch。
4。如有疑问,选择 Kera s
如果不确定,先从 TensorFlow 的 Keras API 开始。PyTorch 的 API 有更多的灵活性和可控性,但是很明显 TensorFlow 的 Keras API 可以更容易上手。如果你正在阅读这篇帖子,我可以假设你正开始深度学习的话题。
此外,您还有我在过去两年中准备的其他出版物中关于 Keras 的额外文档。(一个秘密:我也计划在夏天准备好 PyTorch 的等效文档)。
顺便说一下,Keras 为 2020 年计划了几款“让它更简单”的新产品。这里列出了最近添加或即将推出的一些新功能:
层和预处理 API
到目前为止,我们已经用 NumPy 和 PIL (Python Imaging Library)编写的辅助工具完成了预处理。而且这种外部预处理使得模型的可移植性降低,因为每当有人重用一个已经训练好的模型时,他们都必须重放预处理程序管道。因此,预处理现在可以通过“预处理层”成为模型的一部分。这包括文本标准化、标记化、矢量化、图像标准化、数据扩充等方面。也就是说,这将允许模型接受原始文本或原始图像作为输入。我个人觉得这会很有意思。
Keras 调谐器
这是一个框架,允许您在 Keras 中找到模型的最佳超参数。当你花一些时间在深度学习上工作时,你会看到这解决了模型构建的一个代价高昂的问题,例如细化超参数,以使模型表现最佳。这总是一项非常困难的任务。
AutoKeras
这个项目试图在几行代码中为数据找到一个好的 ML 模型,根据可能模型的空间自动搜索最佳可能模型,并使用 Keras Tuner 查找超参数调整。对于高级用户,AutoKeras 还允许对搜索空间和过程的配置进行更高级别的控制。
云角
我们的愿景是让程序员更容易将代码(在我们的笔记本电脑或 Google Colab 上本地工作的代码)转移到云中,使其能够在云中以最佳和分布式的方式执行代码,而不必担心集群或 Docker 参数。
与 TensorFlow 的集成
与 TFX (TensorFlow Extended,一个管理 ML 生产应用的平台)的更多集成工作正在进行中,并更好地支持将模型导出到 TF Lite(一个用于移动和嵌入式设备的 ML 执行引擎)。毫无疑问,提高对模型生产的支持对于 Keras 中程序员的忠诚度是至关重要的。
5。总而言之
用一个明喻来说,你认为开始编程最好的语言是 C ++还是 Java?嗯……这取决于我们想用它做什么,最重要的是取决于我们有什么工具可以学习。我们可能无法同意,因为我们有一个先入为主的观点,我们很难改变我们对这个问题的答案(PyTorch 和 TensorFlow 的“粉丝”也是如此😉 ).但是我们肯定同意重要的是知道如何编程。事实上,无论我们从一种语言的编程中学到什么,当我们使用另一种语言时,它都会为我们服务,对吗?同样的事情也发生在框架上,重要的是了解深度学习,而不是框架的语法细节,然后我们会将这些知识用于流行的框架或我们当时有更多机会使用的框架。
这篇文章的代码可以从 GitHub 下载
[## TensorFlow vs Pytorch:战斗仍在继续
本周,脸书宣布 PyTorch 1.5 的发布
towardsdatascience.com](/tensorflow-vs-pytorch-the-battle-continues-9dcd34bb47d4)
原文于 2020 年 4 月 19 日 发表于加泰罗尼亚语 https://Torres . ai。
附录 1:对 PyTorch 感兴趣?
初学 PyTorch
towardsdatascience.com](/deep-learning-with-pytorch-a93b09bdae96)
附录二:对 TensorFlow 感兴趣?
[## 卷积神经网络初学者使用 Keras 和 TensorFlow 2
边做边学:包含 GitHub 代码的实用指南
towardsdatascience.com](/convolutional-neural-networks-for-beginners-using-keras-and-tensorflow-2-c578f7b3bf25)
张量流性能分析
在以前的帖子中(这里和这里,我告诉过你我们的团队如何使用亚马逊 SageMaker 和亚马逊 s3 服务来训练我们对大量数据的深度神经网络。
在这篇博客中,我想讨论如何分析在 TensorFlow 中运行的 DNN 训练课程的性能。当谈到 DNN 训练课的“表现”时,人们可能指的是许多不同的事情。在本博客的上下文中,“性能”分析指的是对执行训练的速度(例如,通过每秒迭代次数的训练吞吐量来衡量)的分析,以及会话利用系统资源来实现这一速度的方式。我们将不会提及被训练的模型的性能,其通常通过测试集上的损失或度量评估来测量。性能的另一个衡量标准是训练收敛之前所需的批次数量。这也超出了本博客的范围。
简而言之,如果你想弄清楚为什么你的训练跑得慢,你来对地方了。如果您正在寻找提高 mnist 模型准确性的方法,或者正在寻找使用什么优化器设置来加速收敛,那么您还没有找到。
我们将回顾的例子是用 TensorFlow 编写的,并使用 Amazon SageMaker 服务在云中运行,但是我们将进行的讨论同样适用于任何其他培训环境。
前奏
任何关于你的训练的性能分析的讨论都需要我们清楚目标是什么,或者我们试图优化什么效用函数。您的效用函数可能取决于许多因素,包括您可支配的训练实例的数量、这些实例的成本、您需要训练的模型的数量、项目调度约束等。
为了进行有意义的讨论,我们将做一些简化的假设。我们的目标将是在给定固定训练环境的情况下,最大化训练会话的吞吐量,而不损害最终模型的质量,也不增加收敛所需的训练样本的数量。
如前所述,这个目标包含了一些模糊之处,我们会及时解释。
培训实例
我们的第一个简化假设是,训练是在单个实例/机器上执行的,并且实例类型是固定的。
自然,不同的型号在不同类型的机器上表现不同。在理想的情况下,我们总是能够选择一台对于被训练的模型来说是最优的机器,也就是说,一台所有资源都被充分利用的机器。这样,我们可以避免未被使用的资源成本。不幸的是,在现实世界中,我们通常面临固定数量的实例类型可供选择。例如,Amazon SageMaker 提供了多种多样的实例类型供选择,它们在 GPU 的类型和数量、CPU 的数量、网络属性、内存大小等方面都有所不同。另一方面,人们没有能力自由选择(基于他们的模型的属性)具有特定数量的 CPU、特定的 GPU 和特定的网络带宽的机器。
为了选择最合适的训练实例,必须仔细权衡模型对不同训练实例的适合程度,以及诸如这些实例的成本和可用性以及调度要求等考虑因素。正如我们在本帖中所描述的,这需要对在每种不同的实例类型上训练模型所能达到的最大性能进行全面的分析。
我们将把讨论限制在具有单个 GPU 的实例类型上。具体来说,我们将在配备 NVIDIA V100 Tensor Core GPU 的机器上工作。如果您使用 Amazon SageMaker 服务进行培训,这就是 ml.p3.2xlarge 实例类型。
培训框架
有许多不同的库和框架可用于训练 DNN 模型。和以前一样,固定模型、固定训练算法和固定硬件的训练性能会因不同的软件解决方案而异。正如帖子标题中所强调的,我们的重点将放在 TensorFlow 培训环境上。即使在这种训练环境中,性能也可能取决于许多因素,如框架版本、是否选择高级 API(如 tf.estimator 或 tf.keras.model.fit)或实现自定义训练循环,以及将训练数据输入训练管道的方式。我们在本文中的假设是,训练将在 TensorFlow 2.2 中使用 tf.keras.model.fit() API 执行,数据将使用 tf.dataset APIs 提供。
规则和约束
在没有限制的情况下,加快培训量将是小菜一碟。例如,我们可以简单地减少我们的模型架构的大小,这样培训就会进行。当然,这很可能会扼杀训练的收敛能力,并且所得到的模型预测可能是无用的。显然,我们必须确保我们为加速训练所采取的任何行动都不会损害最终模型的质量。
在我们的讨论中,我们引入了一个额外的约束条件,即我们不希望影响训练收敛所需的总时间,这是通过在收敛之前输入到网络中的训练样本总数来衡量的。换句话说,我们做了一个简化的假设,即我们的模型需要固定数量的训练样本才能收敛,并且我们必须优化的选项不会改变这个固定数量。基于这一假设,给定一个固定的实例和软件环境,我们的目标是优化训练所花费的总时间(以秒为单位),或者,优化训练吞吐量,以训练循环每秒处理的样本数来衡量。
我们强调这是我们为了简化讨论而引入的人为约束。当然,可能存在这样的情况:模型架构的变化会增加收敛所需的样本数量,同时减少收敛所需的总时间。例如,降低某些变量的位精度可能会增加吞吐量,但也可能需要更多步骤才能收敛。如果训练的总时间减少了,并且最终的模型预测也一样好,那么这将是一个很好的优化。另一个重要的例子是增加批量。增加训练批量是增加产量的基本方法之一。但是,有一些模型的训练质量可能对批量大小很敏感。我们做了一个简化的假设,即这种影响可以忽略不计,并且可以通过调整其他超参数(如学习率)来克服。
计量单位
我们将用来测量吞吐量的单位是训练循环每秒处理的样本的平均数。另一种更常见的吞吐量单位度量是每秒的平均训练迭代次数、每秒的步骤数或每秒的批次数。然而,由于这种测量是基于所选择的(全局)批次大小,因此很难在使用不同批次大小的运行之间进行比较。
要提取此测量值,您可以依赖 TensorFlow 打印的日志(在每个时期结束时),也可以自行实施测量值(例如,使用自定义 TensorFlow 回调)。
优化策略
我们提高训练吞吐量的主要方法是提高训练实例的资源利用率。理论上,任何未充分利用的资源都是增加吞吐量的潜在机会。然而,实际上,一旦最昂贵和最重要的系统资源 GPU 得到充分利用,并且假设我们对 GPU 操作的性能感到满意,我们将认为我们的任务已经完成。理论上,我们可以考虑将一些训练卸载给 CPU,但是性能增益可能是微不足道的(如果有的话)——当然不值得头痛。未充分利用可能是管道未达到其最大容量的结果(例如,所有资源都没有得到充分利用)。在这种情况下,增加产量相当容易,(例如,简单地增加批量)。如果管道处于最大容量,系统资源的利用不足通常是管道中瓶颈的结果。例如,在等待 CPU 准备训练数据时,GPU 可能处于空闲状态。在这种情况下,我们将尝试通过消除瓶颈来增加吞吐量。例如,我们可以在 CPU 和 GPU 之间引入并行化,这样 GPU 就永远不必等待数据。
一般策略是反复执行以下两个步骤,直到我们对吞吐量感到满意,或者直到 GPU 得到充分利用:
1.概述培训绩效,以确定流程中的瓶颈和未充分利用的资源
2.解决瓶颈并提高资源利用率
请注意,在任何给定的时间,管道中可能有多个瓶颈。CPU 可能在等待从网络接收数据时处于空闲状态,GPU 可能在等待 CPU 准备用于训练的数据时处于空闲状态。此外,在培训的不同阶段可能会出现不同的瓶颈。例如,当我们保存一个检查点时,GPU 可能被充分利用,但仅在迭代期间空闲。这种瓶颈,即使只是周期性的,仍然可能对平均吞吐量产生重大影响,应该加以解决。
为什么性能分析很重要?
这一点现在应该非常清楚了,但有时显而易见的事情需要陈述。我经常遇到对 40%的 GPU 利用率非常满意的开发人员。让我想尖叫。GPU(通常)是你正在使用的最昂贵的资源;如果你对低于 90%的利用率感到满意,那你就是在浪费你(你公司)的钱。更不用说你可能会更快地交付你的产品。
一切都是为了钱。培训资源是昂贵的,如果你没有最大限度地利用它们,你就是在犯反人类罪。
我希望你能确信描述你的训练表现和最大化资源利用的重要性。在下一节中,我们将回顾典型培训渠道中的一些潜在瓶颈。然后,我们将调查一些我们可以用来识别这些瓶颈的工具。
培训渠道和潜在瓶颈
为了便于讨论培训课程中可能出现的瓶颈,我们提供了一个典型的培训流程图。培训分为八个步骤,每个步骤都可能阻碍培训流程。请注意,虽然在此图中训练是在多个 GPU 上执行的,但在本文中,如上所述,我们将假设只有一个 GPU。
样本培训渠道(按作者)
原始数据输入
除非您是自动生成培训数据,否则您可能会从存储中加载它。这可能来自本地磁盘,也可能通过网络来自远程存储位置。在这两种情况下,您使用的系统资源都有可能阻塞管道。如果每个训练样本的原始数据量特别大,如果您的 IO 接口具有高延迟,或者如果您的训练实例的网络带宽很低,您可能会发现您的 CPU 在等待原始数据进入时处于空闲状态。一个经典的例子是,如果你使用“filemode”和 Amazon SageMaker 一起训练。在“文件模式”中,甚至在训练开始之前,所有的训练数据都被下载到本地磁盘。如果你有很多数据,你可能要等一段时间。另一个 Amazon Sagemaker 选项是使用“pipemode”。这允许您将数据直接从 s3 传输到您的输入数据管道中,从而避免训练启动的巨大瓶颈。但是,即使在“管道模式”的情况下,您也很容易遇到资源限制。例如,如果您的实例类型支持高达每秒 10 千兆位的网络 IO,并且每个样本需要 100 兆位的原始数据,则无论您的 GPU 有多快,您都将拥有每秒 100 个训练样本的上限。解决这些问题的方法是减少原始数据,压缩一些数据,或者选择一个具有更高网络 IO 带宽的实例类型。
在我们给出的示例中,限制来自实例的网络 IO 带宽,但也可能来自您可以从 s3 或其他地方获取的数据量的带宽。(附注:如果您在不使用管道模式的情况下从 s3 中提取数据,请确保选择启用了弹性网络适配器的实例类型。)
数据预处理
训练管道的下一步是数据预处理。在这一阶段,通常在 CPU 上执行,原始数据准备好进入训练循环。这可能包括对输入数据应用扩充、插入屏蔽元素、批处理、过滤等等。tensorflow.dataset 函数包括用于在 CPU 内并行处理操作的内置功能(例如 tf.dataset.map 例程中的 num_parallel_calls 参数),以及用于与 GPU 并行运行 CPU 的内置功能(例如 tf.dataset.prefetch)。然而,如果您在这个阶段运行繁重或内存密集型计算,您可能仍然会发现您的 GPU 空闲,因为它等待数据输入。
更新:请务必查看我的最近的帖子,主题是数据预处理管道中的性能瓶颈。
CPU 到 GPU 的数据传输
在大多数情况下,您的 CPU 和 GPU 使用不同的内存,训练样本需要从 CPU 内存复制到 GPU 内存,然后训练循环才能运行。这也可能导致瓶颈,这取决于数据样本的大小和接口带宽。您应该考虑的一件事是,只要有可能,就推迟转换到更高位表示(tf.cast())或解压缩位掩码(tf.one_hot),直到数据被复制到 GPU 内存之后。
GPU 向前向后
培训管道的核心是向前和向后管道。这个步骤是在 GPU 上执行的。由于 GPU 是最昂贵的资源,我们希望 GPU 始终保持活跃并处于最佳性能。在大多数情况下,平均吞吐量(以每秒训练的样本数表示)会随着批处理大小的增加而增加,因此我们会尝试将批处理大小增加到 GPU 的内存容量。
显然,这一步的吞吐量是你的模型架构和损失函数的函数。减少计算量的常用方法有很多。这里有一个非常小的例子:
相比密集图层,更喜欢 conv 图层
用一系列具有相同感受野的较小的回旋来代替大的回旋
使用低精度或混合精度变量类型
考虑使用 TensorFlow 本机函数,而不是 tf.py_func
优先选择 tf.where 而不是 tf.cond
研究您的模型和层设置如何影响您的 GPU 性能,例如内存布局(通道优先或最后)和内存对齐(层输入和输出大小、通道数量、卷积滤波器形状等),并相应地设计您的模型
自定义图优化
另一件要注意的事情是图形操作,它不受 GPU 支持,因此被卸载回 CPU。有时,您可能会发现在训练步骤的中间,GPU 会将数据发送回 CPU 进行处理,并等到处理后的数据返回后再继续计算。不用说,这会降低您的吞吐量。在这种情况下,您应该使用分析工具来查明有问题的操作,并进行必要的调整,以便它们可以在 GPU 上运行。
梯度共享
仅当在多个 GPU 上训练运行分布式训练时,才执行此步骤,无论是在单个训练实例上还是在多个实例上。我们将分布式培训的讨论留到以后的文章中,但是我们会注意到这一步也可能引入瓶颈。在分布式训练期间,每个 GPU 必须从所有其他 GPU 收集梯度。根据分布策略、梯度的数量和大小以及 GPU 之间通信通道的带宽,GPU 在收集梯度时可能会发现自己处于空闲状态。为了解决这些问题,可以降低梯度的位精度、调整通信信道或考虑其他分发策略。
GPU 到 CPU 的数据传输
在训练过程中,GPU 会向 CPU 返回数据。通常,这将包括损失和度量结果,但也可能周期性地包括更多存储器密集型输出张量或模型权重。和以前一样,这种数据传输可能会在训练的某些阶段引入瓶颈,这取决于数据的大小和接口带宽。
模型输出处理
CPU 可能会对从 GPU 接收的输出数据执行一些处理。这个过程通常发生在 TensorFlow 回调中。这些可以用来评估张量,创建图像摘要,收集统计数据,更新学习率等等。这可能会以不同的方式减少培训量:
如果处理是计算或内存密集型的,这可能会成为性能瓶颈。如果处理独立于模型 GPU 状态,您可能想要尝试在一个单独的(非阻塞的)线程中运行。
运行大量回调也可能成为管道的瓶颈。你可能会考虑把它们合并成一个小数字。
如果您的回调在每次迭代中处理输出,您也可能会降低吞吐量。考虑降低处理的频率,或者将处理添加到 GPU 模型图中(例如,使用定制的张量流度量)。
CPU 到存储
在训练期间,CPU 可能会定期将事件文件、日志文件或模型检查点传输到存储。像以前一样,大量的数据加上有限的 IO 带宽可能会导致训练管道中的延迟。即使您小心翼翼地使数据传输无阻塞(例如,使用专用的 CPU 线程),您也可能使用共享相同有限带宽的网络输入和输出通道(如在 Amazon SageMaker 实例上)。在这种情况下,通过网络输入提供的原始训练数据量可能会下降。
发生这种情况的一种方式是,如果我们将所有的张量流汇总收集在一个事件文件中,该文件在训练过程中不断增长。每次事件文件上传到 s3 时,网络上传输的数据量都会增加。当文件变得非常大时,数据上载会干扰训练。
性能分析工具
现在,我们已经了解了在培训过程中可能会遇到的一些瓶颈,让我们回顾一下您可以使用的一些工具和技术来识别和分析它们。
实例度量
一个好的起点是评估培训实例的资源是如何被利用的。在 Amazon SageMaker 中,这些是作为“实例指标”提供的,显示在培训工作页面的“监控”部分,以及 Amazon SageMaker Studio 中。以下是“培训职务”页面上的一个示例:
实例度量(从 AWS 控制台)
实例指标包括内存利用率、CPU 利用率、网络输入、网络输出、GPU 内存利用率、GPU 利用率和磁盘利用率的图表,其中测量值以五分钟为间隔显示。您可以使用它来确保事情按预期运行,并获得可以改进的高层次指示。(我们称之为“高水平”,因为测量相对较少。)例如,您可以验证您实际上正在 GPU 上运行,并评估您的使用情况。此外,您可以识别不同指标中的异常,例如:
CPU/GPU 利用率下降,这可能表明存在瓶颈
上升的 CPU 内存可能表示内存泄漏
断断续续的网络输入可能表明我们从 s3 获取数据的方式存在问题
如果网络输入达到最大容量(与训练实例属性相比),这可能表示输入管道出现瓶颈
上升的网络输出可能表明我们正在上传一个不断增长的文件,而不是上传小文件
GPU 启动时间的延迟可能表示模型编译时间过长
值得注意的一点是,TensorFlow 的默认行为是占用所有的 GPU 内存。所以不要因为 GPU 内存利用率显示为 100%而过度惊慌(或高兴)。要配置 TensorFlow 只使用它实际需要的内存,您需要应用下面的代码行。
gpus = tf.config.experimental.list_physical_devices('GPU')
**for** gpu **in** gpus:
tf.config.experimental.set_memory_growth(gpu, **True**)
点击“View Instance Metrics”链接会将您带到 CloudWatch 管理控制台,在这里您可以使用度量和图表进行一些操作。例如,您可以放大训练的特定间隔,减少测量间隔(到一分钟),并在单个图表上显示多个指标。
SageMaker 还允许您在该窗口中包含自己的自定义指标。这些被称为“算法度量”。详情见 SageMaker 文档。例如,您可以将培训吞吐量定义为一个指标,并将其显示在此窗口中。
这种性能分析方法还有许多不足之处。除了度量的不频繁,我们很少收到关于代码的哪些部分需要改进的信息。未充分利用的 GPU 可能源于批量较小或瓶颈。高利用率的 GPU 可能是低效损失函数的结果。获取更多的信息需要更多的大炮。
如果您在自己的训练实例上运行,而不是在 SageMaker 服务中运行,那么您将需要自己使用通用的、现成的系统概要分析器来收集“实例度量”。在 linux 环境中,您可以使用命令行工具,如 nmon、perf、strace、top 和 iotop。如果您使用的是 NVIDIA GPU,您可以使用 nvidia-smi(带-l 标志)。
性能分析器
要更深入地了解训练课程不同阶段的表现,您应该使用性能分析器。不同的开发框架可以使用不同的分析器。TenorFlow 提供了 TensorFlow Profiler 用于分析 tf 模型。您第一次接触性能分析器可能会有点害怕。通常有多个图表,看起来可能超载,而且乍一看似乎令人困惑。如何解释报告的大量数据并不总是一目了然。但是一旦掌握了窍门,分析器在评估资源利用率、识别瓶颈和提高模型性能方面非常有用。
张量流剖面仪
查看 TensorFlow Profiler 指南和 TensorBoard Profiler 教程了解如何使用该分析器。当使用 tf.keras.model.fit()进行训练时,集成分析的最简单方法是使用 TensorBoard Keras 回调,如下所示:
tbc = tf.keras.callbacks.TensorBoard(log_dir='/tmp/tb',
profile_batch='20, 24')
这将捕获迭代 20 到 24 的统计数据,并将它们保存到一个可以在 TensorBoard 中打开和分析的文件中。(间隔的大小会影响捕获的数据量。)
请注意,TensorFlow 分析器回调是有限的,因为它在一系列迭代上运行,而不是在整个训练会话上运行。这意味着,要评估特定的迭代(例如,保存检查点的迭代)或了解性能如何随时间变化,您需要运行不同的分析会话,并在它们之间进行比较。或者,您可以使用更高级的分析 API,这将允许您更自由地收集统计信息。
编程技术
有时候,对你的训练表现有一个好的感觉的最好方法是通过摆弄你的程序来看看不同的变化是如何影响表现的。以下是一些你可以尝试的小例子:
您可以使用不同的批处理大小进行训练,以测试最大 GPU 内存容量并评估对 GPU 利用率的影响。
您可以在不执行训练的情况下对训练数据集进行循环,如这里的所述来测量输入管道的最大吞吐量。
您可以使用生成的数据进行训练,以评估最大 GPU 吞吐量。
您可以添加和删除预处理管道的不同部分,以评估它们对吞吐量的影响。
您可以使用不同的、简单的和复杂的损失函数进行训练,以评估损失函数对吞吐量的影响。
有没有复试都可以训练。
如果您想要评估特定函数的影响,请用简单的虚拟函数替换它们来评估影响。
另一种有用的编程技术是简单地添加打印(例如使用 tf.print())和计时器(例如 tf.timestamp())来评估不同代码块的性能。
信息干扰权衡
信息干扰权衡指的是一个简单的观察,即我们为了提取有意义的性能数据而改变原始管道越多,数据实际上就越没有意义。我们越是增加轮询系统利用率指标的频率,实际的分析活动就越是盖过训练循环的活动,基本上认为捕获的数据是无用的。
找到正确的平衡并不总是那么容易。完整的性能分析策略应该包括不同入侵级别的分析,以便尽可能清楚地了解正在发生的情况。
案例研究
在这一节中,我们将展示一些我们在实践中讨论过的潜在性能问题。请注意,我们将展示的许多例子都是由真实事件激发的;我们在 AWS 培训中遇到的实际问题。对于每个示例,我们将演示如何通过选择和分析一些分析度量来识别性能问题。
我们将使用的模型是深度卷积网络,它学习对输入图像执行像素级分割。我们的基本模型将 CPU 和 GPU 处理并行化,以 64 的批处理大小运行。在这种情况下,训练吞吐量大约是每秒 100 个样本。SageMaker 仪表板报告 GPU 内存利用率为 98%,GPU 利用率在 95%到 100%之间。
从 tf profiler 的 trace_viewer 中也可以看到 GPU 的利用率,我们可以看到 GPU 几乎总是处于活动状态。回想一下,tf 配置文件被配置为运行五个步骤,即步骤 20–24。
tf profiler — trace-viewer(作者使用 TensorBoard)
为了便于比较,我们还将注意到实例度量报告每分钟 14.9 的网络输入和每分钟不到 100 的网络输出。
小批量
为了评估批处理大小对性能的影响,我们将批处理大小减少到 2,保持所有其他模型属性不变。吞吐量一直下降到每秒 40 个样本。对 GPU 利用率和 GPU 内存利用率的影响从“实例指标”中可以立即看到,我们看到了显著的下降,分别下降到 60%和 23%左右。自然,每次迭代花费的时间要少得多,但是 GPU 处于活动状态的时间百分比要低得多。
tf profiler 步骤时间图显示,小批量导致一半以上的时间花费在将内核加载到 GPU 上。
小批量— tf profiler 步进时间图(作者使用 TensorBoard)
trace_viewer 显示,虽然 GPU 保持不受输入的阻碍,但 tf ops 似乎稀疏地分散在每个步骤的计算块中。
小批量— tf profiler 跟踪查看器(作者使用 TensorBoard)
在 TensorFlow 2.3 中,引入了一个新的内存分析器工具,允许您识别 GPU 内存的未充分利用,并获得您是否可以安全地增加训练批量的指示。
网络输入瓶颈
在本例中,我们将人为地在网络输入端引入一个网络瓶颈。为此,我们将删除 10 个输入记录中的 9 个,这样我们需要 10 倍的网络输入数据来维持相同的吞吐量。下面是用于执行此练习的代码:
*# id generator* **def** id_gen():
e = 0
**while** **True**:
**yield** e
e += 1*# enter id to feature dict* **def** add_id_label(f, i):
f['id'] = i
**return** f*# create a dataset of indices and zip it with input dataset* ids = tf.data.Dataset.from_generator(id_gen, tf.int64)
ds = tf.data.Dataset.zip((ds, ids))*# enter id to feature dict* ds = ds.map(add_id_label, num_parallel_calls=tf.data.experimental.autotune)*# filter out every 9 out of 10 records* ds = ds.filter(**lambda** f:
tf.squeeze(tf.equal(tf.math.floormod(f['id'],10),0)))
从实例指标中,我们可以看到网络输入的上限为每分钟 33.4,仅比正常运行(14.9)的两倍多一点,尽管我们需要十倍于此的数据量。吞吐量下降到每秒 22 个样本。不出所料,我们的程序高度依赖输入。tf profiler 报告说,在总的步进时间中,77.8%的时间用于等待数据。
网络瓶颈— tf 分析器步进时间图(作者使用 TensorBoard)
trace_viewer 清楚地显示了 GPU 在等待来自 tf_data_iterator_get_next 操作的数据时,每个步骤的大部分时间都处于空闲状态。
网络瓶颈— tf profiler 跟踪查看器(作者使用 TensorBoard)
过载的预处理管道
TensorFlow 提供了最大化数据处理并行化的方法(如 TensorBoard Profiler 教程所示),但这并不能免除您优化输入数据处理管道的责任。如果您的数据处理是资源密集型的,它可能会影响模型的性能,您可能需要考虑将一些处理转移到离线状态。如果繁重的操作是 GPU 友好的(例如大型卷积),您也可以考虑使用实验性的 map_on_gpu 函数在 GPU 上执行操作,(但要记住数据传输的开销)。另一种选择是选择具有更多 CPU 内核的训练实例。
在本例中,我们将通过对输入帧应用滤波器大小为 7x7 的 separable_conv2d 来模拟过载预处理流水线。
吞吐量下降到每秒 25 个样本,最大 GPU 利用率下降到 36%。另一方面,CPU 利用率从 66%跃升至 96%。同样,程序是高度输入绑定的,跟踪查看器显示大量的 GPU 空闲时间。
Preproc 瓶颈— tf profiler 跟踪查看器(作者使用 TensorBoard)
仔细分析 trace-viewer 的 CPU 部分(此处未显示),可以看出可分离卷积占用了大量计算资源。
CPU GPU 数据接口的瓶颈
在本例中,我们通过将输入帧的大小放大 10 倍,人为地增加了传递给 GPU 的输入数据。在 CPU 端,我们简单地复制输入帧 10 次(使用 tf.tile())。在 GPU 上,我们接收放大的输入帧,但立即丢弃添加的数据。
在这种情况下,吞吐量下降到每秒 84 个样本,瓶颈在跟踪查看器上非常明显。
H2D 瓶颈— tf profiler 跟踪查看器(作者使用 TensorBoard)
注意对于每一步, Stream #20(MemcpyH2D) 的块的大小是如何增长的,以及 GPU 计算是如何保持空闲直到块完成。
GPU 效率低下
在本例中,我们通过在计算损失函数之前对模型的输出应用 11x11 卷积滤波器来模拟低效图表的影响。不出所料,吞吐量略有下降,降至每秒 96 个样本。对图形的影响可以在 tf profiler tensorflow stats 页面上查看,我们看到添加的操作成为 GPU 中最耗时的操作。该表为我们提供了关于最繁重操作的信息,我们可以使用这些信息来提高模型性能。
低效图表-张量流统计(作者使用张量板)
重帖处理
在本例中,我们添加了一个回调函数,通过在每次迭代后创建和存储 64 个随机图像来模拟处理模型输出的分段掩码。我们注意到的第一件事是 TensorFlow 打印了以下警告:
WARNING:tensorflow:Method (on_train_batch_end) is slow compared to the batch update (0.814319). Check your callbacks.
此外,吞吐量下降到每秒 43 个样本,GPU 利用率下降到 46%,tf profiler 报告每个时间步长中只有 47%的 GPU 处于活动状态。在跟踪查看器上可以清楚地看到瓶颈,我们看到 GPU 在每一步的后半部分处于空闲状态。
Postproc 瓶颈— tf profiler 跟踪查看器(作者使用 TensorBoard)
总结
正如我们所展示的,分析和优化您的培训课程表现的能力可以节省大量时间和成本。您的 DNN 开发团队应该具备执行此类分析所需的技能。这种分析应该成为你的团队发展方法的一个组成部分,并融入到你的 DNN 培训生命周期中。您的开发计划应该包括一些细节,比如何时运行性能分析、使用什么工具、运行什么类型的测试、测试的侵入性如何等等。
在这篇文章中,我们几乎没有触及性能分析的表面。毫无疑问,有更多的工具和技术、其他种类的瓶颈和其他方法来从您的培训资源中挤出更多的性能。我们的目标仅仅是向你介绍这个世界,并强调它在你日常训练中的重要性。祝你好运!!
TensorFlow 性能:加载模型
有许多方法可以保存训练好的模型。但是哪种格式加载最快呢?
Kolleen Gladden 在 Unsplash 上拍摄的照片
模型的加载时间有时是一个被忽视的质量。然而,我们不能总是依赖我们的模型在缓存中舒适地等待,我们仍然需要快速预测。在这种情况下选择什么格式?最近我遇到了这个问题,我很惊讶两者之间的差异有多大。我希望以下对我的发现的简要总结会对你有所帮助。
测量环境和模型
我不打算对这个话题做全面的研究。相反,我将向你展示我在装有 Arch Linux 和最新的 TensorFlow 2.2 和 Python 3.8 的笔记本电脑上测得的加载时间。(我也试过 TensorFlow 2.3rc0,结果差不多。)我将为您提供完整的脚本,以便您可以在自己选择的环境中重现这些实验。如果你能在这种情况下与我分享结果,我会很高兴。
我对深度卷积神经网络 (CNN)很感兴趣。我挑选了三个参数数量不同的知名模型——MobileNet V2、 DenseNet201 和 Resnet152 V2 。所有这些都是 tf.keras.applications 模块的一部分,很容易用于迁移学习。
可用选项
TensorFlow 2 支持两种基本格式— SavedModel 和 HDF5 。您可以保存整个模型,也可以只保存模型的重量。在后一种情况下,您必须重新创建模型架构,然后加载保存的权重。(为了详尽起见,您可以只保存架构,但是我们目前对这个选项不感兴趣。我们希望能够做出预测。)在官方文档中有很多信息,详情请参考那里。我觉得在这里重新表述是多余的。
小注意,我将save()
方法的include_optimizer
参数设置为False.
,这对加载时间没有太大影响,但是我们的用例中不需要优化器,所以让我们忽略它。
TensorFlow Lite
除此之外, TensorFlow Lite 不得不提。它有自己的格式。由于精简版是为移动、嵌入式和物联网设备设计的,该模型应该更快。用于预测和加载。尤其是当你用强大的服务器代替我们的智能手机的时候。不幸的是,一切都是有代价的。如这里所解释的:
由于 TensorFlow Lite 操作集小于 TensorFlow 的操作集,因此并非每个模型都是可转换的。即使对于受支持的操作,出于性能原因,有时也需要非常具体的使用模式。我们希望在未来的 TensorFlow Lite 版本中扩展支持的操作集。
结果
要了解模型在参数数量和占用空间方面有多大,请查看下表。
所选模型的大小(参数,MiB)。(*不同的解释者,不等于其余者。)
现在,让我们看看装货时间。我还包括了参数的数量,所以您不需要参考上面的表。在“仅重量一栏中,测量的时间用于重新创建模型和加载保存的重量。
所选模型的加载时间。(*不同的解释者,不等于其余者。)
或者你喜欢图表的话,可以在下面图形化的看到。我省略了 TensorFlow lite,因为数量级是如此不同。
需要解释吗?新的 SavedModel 格式为我们提供了比旧的 HDF5 模型更好的改进。(我一直不喜欢custom_objects
这个参数。)不幸的是装载时间非常糟糕。另一方面,TensorFlow lite 在这方面似乎做了很多工作。
我们能优化模型吗?
不直接针对模型尺寸。在官方文档中有不少关于优化的资源。本页面主要关注 TensorFlow Lite,包括模型选择、训练和训练后优化。除此之外,您还可以直接查看适用于标准模型的 TensorFlow 模型优化 Python API 。
您可以使用两种简单的方法:
- 量化:将权重转换为预测减少的类型,
- 修剪:去除无关紧要的权重。
你可以想象,这些方法影响了机器学习循环的许多部分。这包括模型大小,因此也包括加载时间,但主要关注的是预测时间,其副作用是精度可能会降低。
因此,这些技术高度依赖于您问题领域。因此,我不认为我能得出足够一般的结论,对本文有用。你需要尝试。请随意下载我的脚本来测量模型加载时间,并添加您自己的优化模型进行比较。
最后的想法
要记住的关键点很简单,就像博文本身一样。装载时间是一个问题。在你为你的问题选择合适的模型之前,你应该考虑如何使用它。你需要快速启动吗?也许你应该选择 TensorFlow Lite。并选择仅包含受支持操作的模型。有很多,但是如果你忽略了这一部分,最后的惊喜可能不会令人愉快。
希望这篇文章对你有用。请随时评论,批评,提问!谢谢你的关注。
张量流量子:美女与野兽
所以,我们终于来了,在漫长的等待之后,我们将进入一个量子计算的时代。TFQ,张量流之美和量子计算的兽性。
量子计算正在成为 2020 年更近距离观察的技术。我们已经看到了霍尼韦尔、谷歌和其他公司最近的一些公告,值得期待今年会有新的硬件出现。现在,谷歌提出了一个新的机器学习框架,用于以研究为目的的量子计算实验。
来源:https://www.tensorflow.org/quantum/overview
定义量子计算机
1930 年,艾伦·特纳发明了车床。它由无限长的胶带组成,也就是无限长的胶带,再被分成小方块。每个方块可以包含一个符号(1 或 0),也可以是空白。之后,有一个读/写设备读取这些符号,并相应地向机器发出所有指令。这就是我们的传统计算机过去的工作方式。
在量子翻转机中,有一个微小的差异会造成性能上的巨大差距,即磁带。在量子机器中,磁带以量子状态存在。这意味着磁带上的符号可以是 0 或 1。换句话说,这两个符号同时是 0 和 1。虽然普通的图灵机一次只能执行一次计算,但量子图灵机可以一次执行多次计算。
量子计算机不限于两种状态;它们将信息编码为量子位,或量子位,可以叠加存在。
所以这里最重要的是量子位。
量子位代表原子、电子以及它们各自的控制装置,它们一起工作,充当计算机存储器和处理器。因为量子计算机可以同时包含这些多种状态。它有可能比当今最强大的超级计算机强大数百万倍。
量子计算的最新发展
日前, Google AI ,宣布发布 TensorFlow Quantum ,这是一个用于量子机器学习模型快速成型的开源库。他们并不孤单,因为他们得到了滑铁卢大学和大众汽车的帮助。
TensorFlow Quantum 的核心思想是在 TensorFlow 编程模型中交错量子算法和机器学习程序。谷歌将这种方法称为量子机器学习(QML),并能够通过利用其最近的一些量子计算框架来实现它,如谷歌 Cirq 。
所以这里的主要问题是:
cirq 是什么?
Cirq 是一个为设备调用量子电路的开源框架。cirq 背后的主要思想是提供一个简单的编程模型,抽象出量子计算的基本构件。cirq 的基本结构包含量子位、门、测量算子,当然还有电路,这些都是量子计算所必需的东西。
根据 Google AI:“Cirq 使研究人员能够为特定的量子处理器编写量子算法。Cirq 为用户提供了对量子电路的微调控制,使用本机门指定门的行为,将这些门适当地放置在设备上,并在量子硬件的限制内安排这些门的时序”
因此,cirq 中包含以下关键构建模块:
- 电路:量子电路的基本形式由这些电路来表示。一个 Cirq 电路被表示为一个矩的集合,其中包括在一些抽象的时间滑动期间可以在量子位上执行的操作。
- Gates: Gates 对量子位集合的抽象操作。
- 时间表和设备:简单来说,时间表由一组操作和时间表运行设备的描述组成。在量子计算方面,包括了更多关于门的时间和持续时间的详细信息。
张量流量子
TensorFlow Quantum 是一个用于混合量子-经典机器学习的库,开发/构建量子机器学习应用程序。它允许我们构建量子数据集,量子模型。
TFQ 提供了一个模型,抽象了与张量流、Cirq 和计算硬件的交互。栈顶是要处理的数据。经典数据由 TensorFlow 原生处理;TFQ 增加了处理量子数据的能力,包括量子电路和量子算符。堆栈的下一层是 TensorFlow 中的 Keras API。因为 TFQ 的核心原则是与核心 TensorFlow 的本机集成,特别是与 Keras 模型和优化器的集成。在 Keras 模型抽象的下面是我们的量子层和微分器,当与经典张量流层连接时,它支持混合量子-经典自动微分。在这些层和竞争优势之下,TFQ 依赖于 TensorFlow ops,它实例化了数据流图。
来源:https://www.tensorflow.org/quantum/overview
从执行的角度来看,TFQ 遵循以下步骤来训练和建立 QML 模型。
- 准备一个量子数据集:量子数据作为张量加载,指定为 Cirq 中写的量子电路。张量由 TensorFlow 在量子计算机上执行,生成量子数据集。
- 评估量子神经网络模型:在这一步中,研究人员可以使用 Cirq 构建一个量子神经网络原型,稍后他们会将它嵌入 TensorFlow 计算图中。
- 样本或平均值:该步骤利用了对涉及步骤(1)和(2)的几次运行求平均值的方法。
- 评估经典神经网络模型:该步骤使用经典深度神经网络来提取在先前步骤中提取的度量之间的相关性。
- 评估成本函数:类似于传统的机器学习模型,TFQ 使用这个步骤来评估成本函数。如果量子数据被标记,这可以基于模型执行分类任务的准确程度,或者如果任务是无监督的,则基于其他标准。
- 评估梯度&更新参数 —在评估成本函数后,管线中的自由参数应在预期降低成本的方向上更新。
TFQ 代表了该领域最重要的里程碑之一,并且利用了量子和机器学习领域的一些最佳知识产权。关于 TFQ 的更多细节可以在项目网站上找到。
最初发布于https://blog . knol dus . com/tensor flow-quantum-beauty-and-the-beast/
你们认为量子计算的未来会是怎样的?请在评论框中告诉我们…
快乐学习:-)
张量流量子:将机器学习与量子计算结合起来
谷歌开源了其新的量子计算机器学习库,名为 TensorFlow Quantum (TFQ)。
图片来源: Pixabay
介绍
2020 年 3 月 9 日, Google AI ,宣布发布 TensorFlow Quantum (TFQ),这是一个用于量子机器学习模型快速成型的开源库。他们并不孤单,因为他们得到了滑铁卢大学和大众汽车的帮助。
这个新框架为量子计算(QC)研究人员提供了必要的软件和设计工具,将机器学习的能力和可能性带入量子计算领域。
这是他们在量子计算软件平台中产品的自然发展。2018 年 3 月,谷歌公布了 Cirq ,这是一个用于噪声中等规模量子( NISQ )算法的开源框架。在最基本的层面上,TFQ 将 Cirq 与广受欢迎的深度学习框架 TensorFlow 相集成,并为混合量子经典模型的设计和实现提供了强大的类、数据结构和方法。一方面,它具有与tensor flow API兼容的量子计算抽象,另一方面,它拥有随时可用的高性能量子电路模拟器。
但是它将帮助解决什么样的问题,它关注什么?
我们将很快讨论这个问题。然而,在进一步发展之前,这里有一个惊人的视频,以一种令人难以置信的有趣方式在高层次上介绍了量子计算的核心概念。
TFQ 提供了必要的工具,将量子计算和机器学习研究社区聚集在一起,以控制和模拟自然或人工量子系统。
NISQ:物理科学的新前沿
约翰·普雷斯基尔,加州理工学院理论物理学教授理查德·p·费曼在其颇具影响力的论文“NISQ 时代及以后的量子计算”中指出,“我们现在正处于探索物理科学新前沿的早期阶段,我们可以称之为复杂性前沿或纠缠前沿”。
他继续进一步解释这一点——“现在,人类历史上第一次,我们获得并完善了构建和精确控制许多粒子的非常复杂、高度纠缠的量子状态的工具,这些状态如此复杂,以至于我们无法用最好的数字计算机模拟它们,也无法用现有的理论工具很好地描述它们的特征。这种新兴能力将为新发现打开大门。
近期(不完善的)量子器件
量子计算和/或信息处理系统有着漫长而复杂的历史。今天,在 2020 年,几乎毫无疑问的是,制造必要数量的高质量量子位和量子门的实用方法的商业化还需要几十年的时间。这主要是由于这些系统固有的噪声问题。这种噪声使得量子系统的真正缩放(到更大数量的门)变得极其困难,并且使得量子计算设备变得不稳定和不可靠。
然而,研究人员对近期量子设备的强大用途和可能性越来越感兴趣。这些尺寸适中的设备(50-100 量子位)被称为噪声中等规模量子(NISQ),主要是为固定应用而设计的,可以忍受伴随它们的噪声缺陷。
从热硅点量子位到离子阱,科学家们正在研究创新的方法来构建更复杂和更强大的…
www.sciencemag.org](https://www.sciencemag.org/features/2019/11/quantum-technology-comes-age)
这类算法和装置提供了模拟和分析从经典计算观点来看难以处理的系统特性的真正可能性。
这听起来很有趣!但是,有哪些这样的系统的例子,它们的工作不可能用经典的计算机/算法来模拟,但是这种 NISQ 技术正好适合呢?
利基但有价值的应用领域
对于量子设备的许多应用,如密码术,噪声造成了严重的限制,并可能导致不可接受的误差水平。NISQ 设备不太可能在短期内用于此类应用。因此,金融和商业安全系统中的人们还不需要担心。就目前而言,像 RSA-128 这样久经考验的算法是安全的。
但是密码学并不是量子计算唯一可能的应用领域。NISQ 设备和算法使得深入探索复杂分子和先进纳米材料的特性成为可能。它们还允许高精度和高保真度模拟极端物理,如粒子-粒子相互作用,或高能原子碰撞。
人们可以想象这些能力可能应用于各种各样的领域…
- 药物发现
- 纳米材料合成
- 基因工程和个性化医疗
- 化学工程和材料科学
- 粒子物理学
- 半导体和电子设备
- 光纤、光子学和高速通信
NISQ 系统的渐进和稳定的进展已经让科学家、研究人员和企业高管对这些应用(以及更多)感到兴奋。然而,需要认识到的是,随着 TFQ 等工具包的发布,所有这些应用现在都可以充分利用深度神经网络的强大机制,并将基础物理与大数据的力量相结合。
这类算法和装置提供了模拟和分析从经典计算观点来看难以处理的系统特性的真正可能性。
TFQ 关注的是什么?
具有近期目标的开源理念
量子计算的前景有着漫长而复杂的历史。然而,越来越多的研究人员呼吁 T2 关注可以在 5 年时间范围内解决的实际问题。TFQ 在一个合适的时机被释放,以利用这一运动和研究势头。
如上所述,TFQ 是围绕 Cirq 构建的,Cirq 是为探索中等规模、高噪声的 QC 架构是否能够解决经典数字计算机器难以解决的实际问题而设计和优化的。尊重开源哲学的传统,Cirq 是在 Apache 2 许可下发布的。未来的开源 QC 框架可以利用并嵌入 Cirq 方法和工具,而没有任何版权限制。
优化量子电路的数据结构
各种电路设计原语、逻辑门的灵活行为和功能描述、对高度优化的时序和布局设计工具的访问,是任何数字设计框架的一些期望特征。
量子逻辑设计在这些方面没有什么不同。像 Cirq 这样的工具正好为研究人员提供了这些。在 Cirq 中,谷歌声称,数据结构已经被优化用于编写和编译大量的量子电路描述,以允许用户从现代 NISQ 架构中获得最大收益。作为一个开源项目,它在不断地发展和添加新的抽象和工具。本地支持本地模拟和与未来量子硬件或云上更大模拟器的轻松集成。
特别关注化学
在谷歌宣布 Cirq 的同时(2018 年),他们还宣布了 OpenFermion 和 OpenFermion-Cirq 的上市。
Github Repo for OpenFermion-Cirq。
根据这篇谷歌人工智能博客,“OpenFermion 是一个为化学问题开发量子算法的平台,OpenFermion-Cirq 是一个开源库,将量子模拟算法编译到 Cirq ”。
谷歌的量子人工智能团队已经用新的量子算法进行了一段时间的实验,特别是,他们已经探索了开发低复杂度算法的可能性,用于量子化学领域的快速原型制作。OpenFermion 建立在这些进步的基础上,帮助用户将化学问题的细节(例如分子动力学或反应能量学)转化为优化的量子电路。这些电路也可以调整和定制,以运行在特定的硬件(如电子,光子或分子)。
图片来源: Pixabay
数据结构已经针对编写和编译大量量子电路描述进行了优化,以允许用户充分利用现代 NISQ 架构。
量子数据的解开
NISQ 处理器生成的量子力学数据本身就有噪声,并且通常纠缠(在测量之前)。然而,量子 ML 技术的最大效用之一是它们能够应用于这种嘈杂和纠缠的数据,以促进经典信息的提取,这可以用任何数字计算架构来处理。
基于这些技术,TFQ 图书馆促进了强大模型和工作流的开发,这些模型和工作流解开并概括了量子数据中的相关性。一方面,这鼓励研究人员提高新量子架构的质量和复杂性,另一方面,它加快了新量子算法的发展。
图片来源: Pixabay 。
混合量子经典工作流
由于其相当有限的尺寸(即门数)和噪声行为,NISQ 处理器仍然需要与经典处理器在一个紧密的循环中工作,以产生有意义的计算输出。对于 ML 工作负载,这意味着采用与经典深度学习模型紧密耦合的 NISQ 处理器的循环。混合方法至关重要。
现在,在经典 ML 的世界里,TensorFlow 是最受尊敬和广泛使用的平台之一。它支持跨越各种计算硬件的异构和千万亿次计算——CPU、GPU、TPU,甚至 FPGAs 或定制 ASICs。因此,毫不奇怪,Google 选择这个框架来与 Cirq 平台集成,从而将 QC 工具的范围扩展到最广泛的硬件架构。
这是一个奇妙的世界,编译器、调度器和优化器与量子位、量子门、电路和测量算子无缝融合。可以使用标准 Keras 函数来完成受监督的 ML 训练,并且通过标准 TensorFlow Ops 来获得演化成经典概率分布的量子测量的最终结果。
从谷歌的白皮书来看,这是工作流程的样子,
图片来源:https://ai . Google blog . com/2020/03/announcing-tensor flow-quantum-open . html
有关更多详细信息,请参考这篇综合论文,
[## TensorFlow Quantum:量子机器学习的软件框架
我们介绍 TensorFlow Quantum (TFQ),这是一个开源库,用于混合量子-经典…
arxiv.org](https://arxiv.org/abs/2003.02989)
可以使用标准 Keras 函数来完成受监督的 ML 训练,并且通过标准 TensorFlow Ops 来获得演化成经典概率分布的量子测量的最终结果。
摘要
这是一个激动人心的时刻。
一方面,由于机器学习和人工智能工具和算法辅助下的数据驱动智能的大规模爆炸,科学、技术、商业和社会正处于变革的风口浪尖。
另一方面,物理科学的传统前沿(粒子物理学和宇宙学——对超小和超大的研究)正在让位于对量子设备和信息系统形式的超复杂的研究。
我们只能希望,这两股如此强大而深远的技术潮流的融合,对人类社会和整个地球生命的进步是一个好兆头。
像 TFQ 这样的开源计算平台的发布只是这个旅程中的一小步,但肯定不会是最后一步。
如果您有任何问题或想法要分享,请联系作者在tirthajyoti【AT】Gmail . com。此外,您可以查看作者的 GitHub 资源库中的代码、想法和机器学习和数据科学方面的资源。如果你和我一样,对人工智能/机器学习/数据科学充满热情,请随时在 LinkedIn 上添加我或在 Twitter 上关注我。
[## Tirthajyoti Sarkar - Sr .首席工程师-半导体、人工智能、机器学习- ON…
通过写作使数据科学/ML 概念易于理解:https://medium.com/@tirthajyoti 开源和有趣…
www.linkedin.com](https://www.linkedin.com/in/tirthajyoti-sarkar-2127aa7/)
张量流:20 分钟内发现讽刺
从头开始在 TensorFlow 中构建您的第一个 NLP 模型
Joshua Hoehne 在 Unsplash 上拍摄的照片
这是 TensorFlow 中的一个快速自然语言处理(NLP)教程。NLP 是人工智能的一个子领域,涉及通过计算机理解、解释和操纵人类语言。TensorFlow 是 Google 的开源机器学习库。本教程假设对 Python 有中级了解,对机器学习有基本了解,要求 Python 3 配 TensorFlow 2.x,本教程结束后,你将能够训练自己的挖苦检测模型。
我们想建立一个神经网络分类器来检测文本中的讽刺。为此,我们将使用‘rish abh Misra 的新闻标题数据集中的讽刺’。以下代码解析数据集-
# Importing required libraries
import json
import tensorflow as tf
import requests
import numpy as np
import pandas as pd
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences# Get the dataset
srcsm_json = requests.get('[https://storage.googleapis.com/laurencemoroney-blog.appspot.com/sarcasm.json'](https://storage.googleapis.com/laurencemoroney-blog.appspot.com/sarcasm.json'))# Inspecting the data, print 450 characters
print(srcsm_json.text[0:450])>>> [
>>> {"article_link": "https://www.huffingtonpost.com/entry/versace- >>> black-code_us_5861fbefe4b0de3a08f600d5", "headline": "former
>>> versace store clerk sues over secret 'black code' for minority >>> shoppers", "is_sarcastic": 0},
>>> {"article_link": "https://www.huffingtonpost.com/entry/roseanne- >>> revival-review_us_5ab3a497e4b054d118e04365", "headline": "the >>>'roseanne' revival catches up to our thorny political mood, for >>> better and worse", "is_sarcastic": 0},# Separate the json into sentences and labels
sentences = []
labels = []for item in srcsm_json.json():
sentences.append(item['headline'])
labels.append(item['is_sarcastic'])print(pd.DataFrame({'sentence' : sentences[0:10], 'label':labels[0:10]}))>>> sentence label
>>> 0 former versace store clerk sues over secret 'b... 0
>>> 1 the 'roseanne' revival catches up to our thorn... 0
>>> 2 mom starting to fear son's web series closest ... 1
>>> 3 boehner just wants wife to listen, not come up... 1
>>> 4 j.k. rowling wishes snape happy birthday in th... 0
>>> 5 advancing the world's women 0
>>> 6 the fascinating case for eating lab-grown meat 0
>>> 7 this ceo will send your kids to school, if you... 0
>>> 8 top snake handler leaves sinking huckabee camp... 1
>>> 9 friday's morning email: inside trump's presser... 0
我们有了句子的初始数据集和相应的标签 1 或 0,表明该句子是否是讽刺性的。讽刺分类器将使用句子作为输入,并预测标签。在我们开始训练分类器之前,我们需要以计算机可以处理的方式来表示输入数据,即句子。
这是通过标记化和排序的结合来实现的。标记化是一项将字符串分割成多个部分(称为标记)的任务,同时丢弃某些字符,如标点符号。标记化为每个唯一的单词分配一个数字。排序建立在标记化的基础上,将句子表示为一系列数字。
在实践中,我们的分类器可能会遇到它在训练数据中没有见过的单词,在这种情况下,分类器将完全忽略这些单词,从而导致一些信息丢失。为了最大限度地减少信息损失,在标记化过程中,我们分配一个标记来表示所有看不见的(词汇之外的)单词。对于处理可变长度句子的神经网络,我们使用填充。在填充中,我们设置了句子的最大允许长度,所有短于最大长度的句子将被填充以匹配最大长度。相反,所有超过最大长度的句子将被截断以匹配最大长度。以下代码执行标记化和排序-
# Splitting the dataset into Train and Test
training_size = round(len(sentences) * .75)training_sentences = sentences[0:training_size]
testing_sentences = sentences[training_size:]
training_labels = labels[0:training_size]
testing_labels = labels[training_size:]# Setting tokenizer properties
vocab_size = 10000
oov_tok = "<oov>"# Fit the tokenizer on Training data
tokenizer = Tokenizer(num_words=vocab_size, oov_token=oov_tok)
tokenizer.fit_on_texts(training_sentences)word_index = tokenizer.word_index# Setting the padding properties
max_length = 100
trunc_type='post'
padding_type='post'# Creating padded sequences from train and test data
training_sequences = tokenizer.texts_to_sequences(training_sentences)
training_padded = pad_sequences(training_sequences, maxlen=max_length, padding=padding_type, truncating=trunc_type)testing_sequences = tokenizer.texts_to_sequences(testing_sentences)
testing_padded = pad_sequences(testing_sequences, maxlen=max_length, padding=padding_type, truncating=trunc_type)
在这个阶段,我们准备根据我们的数据训练一个神经网络,我们有代表句子的数字序列。为了确定序列中的某些内容是否具有讽刺意味,我们使用了嵌入的概念。假设我们用向量来表示单词,比如单词‘good’用(1,0)来表示,与 good 相反的单词‘bad’用(-1,0)来表示。表示积极意义的词将接近(1,0),而表示消极意义的词将接近(-1,0)。如果我们将这个框架扩展到多个维度,一个句子可以由句子中单词的向量之和来表示。当神经网络分类器学习检测讽刺时,它在这些多维度中学习方向。讽刺的句子会有很强的讽刺方向的成分,其他的句子会有很强的不讽刺方向的成分。随着我们用更多数据训练神经网络分类器,这些方向可能会改变。当我们有一个经过充分训练的神经网络分类器时,它可以对句子中所有单词的向量求和,并预测句子是否是讽刺性的。
最终的神经网络由学习每个单词方向的顶层嵌入组成,下一层是将向量相加的全局平均池,下一层是深度神经网络,最后一层是返回句子讽刺的概率的 sigmoid 层。以下代码训练神经网络-
# Setting the model parameters
embedding_dim = 16model = tf.keras.Sequential([
tf.keras.layers.Embedding(vocab_size, embedding_dim, input_length=max_length),
tf.keras.layers.GlobalAveragePooling1D(),
tf.keras.layers.Dense(24, activation='relu'),
tf.keras.layers.Dense(1, activation='sigmoid')
])model.compile(loss='binary_crossentropy',optimizer='adam',metrics=['accuracy'])model.summary()>>> Model: "sequential_4"
>>> ________________________________________________________________
>>> Layer (type) Output Shape Param #
>>> ================================================================
>>> embedding_4 (Embedding) (None, 100, 16) 160000
>>> ________________________________________________________________
>>> global_average_pooling1d_4 ( (None, 16) 0
>>> ________________________________________________________________
>>> dense_8 (Dense) (None, 24) 408
>>> ________________________________________________________________
>>> dense_9 (Dense) (None, 1) 25
>>> ================================================================
>>> Total params: 160,433
>>> Trainable params: 160,433
>>> Non-trainable params: 0
>>> ________________________________________________________________ # Converting the lists to numpy arrays for Tensorflow 2.x
training_padded = np.array(training_padded)
training_labels = np.array(training_labels)
testing_padded = np.array(testing_padded)
testing_labels = np.array(testing_labels)# Training the model
num_epochs = 30history = model.fit(training_padded, training_labels, epochs=num_epochs, validation_data=(testing_padded, testing_labels), verbose=2)>>> Train on 20032 samples, validate on 6677 samples
>>> Epoch 1/30
>>> 20032/20032 - 2s - loss: 0.6742 - accuracy: 0.5669 - val_loss: >>> 0.6247 - val_accuracy: 0.6629
>>> Epoch 2/30
>>> 20032/20032 - 1s - loss: 0.4758 - accuracy: 0.8121 - val_loss: >>> 0.4018 - val_accuracy: 0.8278
>>> Epoch 3/30
>>> 20032/20032 - 1s - loss: 0.3293 - accuracy: 0.8686 - val_loss: >>> 0.3708 - val_accuracy: 0.8327
>>> ...
>>> ...
>>> Epoch 29/30
>>> 20032/20032 - 1s - loss: 0.0310 - accuracy: 0.9920 - val_loss: >>> 0.9636 - val_accuracy: 0.8167
>>> Epoch 30/30
>>> 20032/20032 - 1s - loss: 0.0297 - accuracy: 0.9925 - val_loss: >>> 0.9431 - val_accuracy: 0.8131
神经网络能够在训练数据集上实现 99%的准确度,在测试数据集上实现 81%的准确度。
让我们通过使用该模型预测两个新句子来进行最后的健全性检查。
sentence = ["Coworkers At Bathroom Sink Locked In Tense Standoff Over Who Going To Wash Hands Longer",
"Spiking U.S. coronavirus cases could force rationing decisions similar to those made in Italy, China."]
sequences = tokenizer.texts_to_sequences(sentence)
padded = pad_sequences(sequences, maxlen=max_length, padding=padding_type, truncating=trunc_type)
print(model.predict(padded))>>>[[0.99985754]
>>> [0.0037319 ]]
该模型预测,“浴室洗手池的同事在谁洗手时间更长的问题上陷入紧张对峙”的概率非常高(几乎为 1 %),而“美国冠状病毒病例激增可能迫使类似意大利和中国做出的配给决定”的概率非常低(几乎为 0 ),这是讽刺。我们可以得出结论,我们的模型正在按预期工作。
本教程基于 YouTube 上 TensorFlow 的“自然语言处理(NLP 零到英雄)”系列。这个教程的 Jupyter 笔记本可以在这里找到。
如果你觉得我们的工作有帮助,请考虑引用
@article{aadityaubhat2020sarcasm-detection,
title={TensorFlow: Sarcasm Detection in 20 mins},
author={Bhat, Aaditya},
journal={Towards Data Science},
year={2020},
url={[https://towardsdatascience.com/create-geo-image-dataset-in-20-minutes-4c893c60b9e6](/tensorflow-sarcasm-detection-in-20-mins-b549311b9e91)}
}
张量流半监督对象检测体系结构
测试整体模型性能时自动标记图像的简单方法。
托拜厄斯·凯勒在 Unsplash 上的照片
目标检测是当今最流行和最常用的计算机视觉方法之一,其目的不仅在于以与最常见的分类问题相同的方式确定是否在图像中找到目标,还在于指出这些感兴趣的目标的位置,这是在多个目标可能同时出现在图像中的情况下的必要方法。
这种方法的挑战之一是创建数据集,一旦需要手动设置图像中所有对象的位置,在大量的观察中花费大量的时间来这样做。
这一过程效率低、成本高、耗时长,主要表现在一些需要在每幅图像中标注几十个物体或者需要专门知识的问题上。
基于此,我创建了 TensorFlow 半监督对象检测架构(TSODA)来交互式地训练对象检测模型,并使用它来基于置信度阈值水平自动标记新图像,将它们聚合到后期的训练过程中。
在本文中,我将向您展示在您的对象检测项目中重现这种方法的必要步骤。有了它,您将能够在测量模型性能的同时自动在图像中创建标签!
目录:
- 措达如何工作
- 示例应用
- 实施
- 结果
- 结论
TSODA 的工作原理
工作类似于任何其他半监督方法,其中使用标记和未标记的数据进行训练,不像最常见的监督方法。
使用手动完成的强标记数据训练初始模型,从这些数据中学习一些特征,然后在未标记数据中创建推断,以将这些新标记的图像聚集到新的训练过程中。
整个想法可以用下图来说明:
(字体:作者)
这个操作一直进行到达到停止标准,或者是执行的次数,或者是没有剩余的未标记数据。
正如我们在模式中看到的,最初配置了 80%的置信度阈值。一旦新图像将被用于新的训练过程,这是一个重要的参数,并且如果不正确地标记,可能产生不期望的噪声,破坏模型性能。
TSODA 的目的是介绍一种简单快速的方法,在你的目标检测项目中使用半监督学习。
示例应用程序
为了举例说明这种方法并测试是否一切正常,我们对 Asirra 数据集的 1,100 幅图像进行了随机抽样,每类样本占 50%。
这些图像被手动标记,以便稍后进行比较,你可以在 Kaggle 上下载相同的数据。
我使用单次多盒探测器(SSD)作为对象检测架构,Inception 作为基础网络,而不是像在原始论文中的 VGG 16。
SSD 和 Inception 在训练速度和准确性之间有很好的权衡,所以我认为这是一个很好的起点,主要是因为在每次迭代中,TSODA 需要保存训练模型的检查点,推断新的图像并加载模型以再次训练它,所以更快的训练有利于更多地迭代并将这些图像聚合到学习中。
测试性能
为了测试 TSODA 的性能,只提供了每个类别的 100 个标记图像来分成训练和测试,同时让 900 个图像作为未标记的,模拟只花很少时间来创建标记数据集的情况。将获得的结果与用所有手动标记的图像训练的模型进行比较。
数据被随机分成 80%的图像用于训练,20%用于测试。
履行
顾名思义,整个架构是使用 TensorFlow 环境完成的,在 2.x 版本中。
这个新的 TF 版本还不完全兼容对象检测,有些部分很难适应,但在接下来的几个月里,这将是所有项目中默认的和更多使用的 TF 版本,这就是为什么我认为调整代码以使用它是重要的。
为了创建 TSODA,新的脚本和文件夹被添加到 TF Model Garden repository 的一个分支中,这样你就可以很容易地克隆和运行你的半监督项目,除了为那些使用 TF 的人提供一个熟悉的结构之外。
你可以克隆我的库来轻松地遵循这些步骤或者修改你的 TF 模型库。
这项工作是在models/research/object _ detection、中完成的,在这里您会找到以下文件夹和文件:
- inference _ from _ model . py:该文件将被执行以使用模型来推断新的图像。
- generate _ XML . py和generate _ TF record . py:都将用于创建训练和测试 TF 记录,用于对象检测模型的训练(这些脚本改编自 浣熊数据集 )。**
- test_images 和 train_images 文件夹:有将要使用的 JPG 图像和 XML 文件。
- 未标记 _ 图像 和 标记 _ 图像 文件夹:分别包含所有未标记的图像和算法自动标记的图像,这些图像将被分为训练和测试文件夹以保持比例。
在 utils 文件夹中,我们还有一些东西:
- generate _ xml . py:该脚本负责获取模型推理并生成一个新的 XML,该 XML 将存储在label _ images文件夹中。
- *visualization _ utils . py*:这个文件在代码上也有一些修改,用来捕捉模型推断,并传递给“generateXml”类。
就是这样,这就是你需要在你的存储库中拥有的一切!
准备环境
运行这个项目你不需要任何东西!?****
训练过程是在一个谷歌 Colab 笔记本电脑,所以它的快速和简单的训练你的模型,你真的只需要用你的图像替换我的图像,并选择另一个基本模型,如果你和。
将原始 Colab 笔记本复制到您的 Google Drive 并执行它。
如果你真的想在你的机器上运行 TSODA,在 Jupiter notebook 的开始你会看到安装要求,只要按照它做,但不要忘记也安装 TF 2.x,我建议创建一个虚拟环境。
理解代码
推理 _ 自 _ 模型. py 负责加载在训练中创建的保存 _ 模型. pb 并使用它在未标记图像中进行新的推理。大部分代码是从 colab_tutorials 文件夹中的object _ detection _ tutorial . ipynb获得的。
如果您不想使用 Colab 进行训练,您需要替换文件开头的路径。
该文件中的另一个重要方法是 partition_data ,它负责将推断的图像(将在标签 _ 图像文件夹中)分割成训练和测试,以保持相同的比率。
您可能想做的一个更改是分流比,在我的例子中,我选择了 80/20 的比例,但如果您想要不同的东西,您可以在方法参数中设置它。
visualization_utils.py 是边界框被绘制到图像中的地方,因此我们使用它来获取框的位置、类名、文件名,并将其传递到我们的 XML 生成器中。下面的代码展示了这个过程的大部分:
如果在图像中检测到一个比指定可信度更高的框,则生成 XML。
所有信息到达 generate_xml.py 中,xml 是使用 ElementTree 创建的。
在代码中,有一些注释可以帮助你理解一切是如何工作的。
结果
为了评估模型性能,使用了平均精度(mAP),如果您对其工作原理有疑问,请查看。
第一个测试是通过 4000 个时期训练一个模型,使用所有强烈标记的图像。
训练持续大约 21 分钟,结果如表 1 所示。
表 2:使用正确标记的所有图像进行训练和测试的地图。(字体:作者)
正如预期的那样,该模型获得了高地图,主要是在较低的 UoI 率。
第二个测试是使用相同的配置完成的,但是 TSODA 只考虑了 100 个带标签的图像。在每次迭代中,该模型由 1000 个时期训练,然后用于推断和创建新的标记图像。结果如图 2 所示。
TSODA 中的模型收敛(font: Author)
整个训练过程用了三十八分钟,比上一次多了十七分钟左右,模型达到了更差的最终图,如表 2 所示:
第一次测试的最终地图。(字体:作者)
如表 3 所示,大多数图像在第一次迭代中被成功注释,在训练中被聚集。这可能意味着最小置信度阈值不够高,因为在前一千次迭代中,模型还没有正确收敛,可能会创建错误的注释。
迭代后剩余的未标记图像的数量(font: Author)。
TSODA 需要更多的时间和历元来提高模型性能并接近原始方法。发生这种情况的原因是,一旦模型需要学习如何概括新模式,在训练集中添加新图像会导致 mAP 丢失,如图 2 所示,其中 mAP 随着新图像的加入而减少,然后在模型学习新特征时再次开始增加。
在图 3 中,有一些图像自动注释的例子。值得注意的是,有些标签没有被很好地标记出来,但是这足以保证给模型提供更多的信息。
自动注释图像的示例。正如所见,如果由人来做,标签可能更适合对象。
考虑到不同的历元增量行为以及更高的置信度阈值,执行了一些新的实验。结果如表 4 所示:
使用第二种配置的结果!(字体:作者)
将置信度阈值设置为 90%可确保在预测中获得正确标签的几率更高,这是模型收敛的一个重要因素。虽然在初始迭代中对 2,500 个时期进行训练,而不是在第一次迭代中只对 1,000 个时期进行训练,因为第一次迭代是大多数图像被标记的地方,所以模型需要学习更多的特征,并且能够击败更高的置信度。在第一次迭代之后,随后的迭代增加 1,500 个历元,直到限制为 8,500 个。这些新配置改善了最终结果。
根据感兴趣对象的种类及其复杂性,TSODA 的性能可能会有所不同。如果通过更多的时期进行训练或者设置更高的置信度阈值,结果可以得到改善,缺点是增加了训练时间。此外,通过迭代的历元增量必须根据问题而改变,以基于未标记图像的数量和阈值来控制模型收敛。
然而,这是一个很好的选择,一旦训练时间比需要人的手动标记时间更便宜,TSODA 的构建方式是,只需少量修改,就可以从头开始训练一个全新的大规模模型。
自动创建的标签也可以在一些图像中手动调整,这可以提高整体性能,并且比手动创建所有标签更快。
结论
所提出的 TSODA 可以在为未标记图像创建新标记方面取得令人满意的结果,达到与强标记训练方法类似的结果,但需要相当少的人力。该解决方案还适用于任何其他 CNN 检测器架构,实施简单快速,有助于数据集创建过程,同时测量物体检测器的整体性能。
参考
关于这个半监督项目的更多细节和背景,请看我的预印本。
Tensorflow 与 Docker 一起使用
张量流框架
如何将 ML 模型部署到生产中?
约瑟夫·巴里恩托斯在 Unsplash 上拍摄的照片
本文将指导您如何构建和训练一个简单的 CNN 模型,然后使用这个训练好的模型作为 Tensorflow 服务的端点。无论您是业余爱好者、ML 工程师、数据科学家还是 DevOps 人员,我相信在本文结束时您将能够部署 ML 模型。
Tensorflow 服务的背景
Tensorflow Serving 是谷歌为生产机器学习系统设计的 API,谷歌和许多大型科技公司广泛使用这种 API。它使得用相同的服务器架构和 API 部署您的模型变得容易。它最适用于张量流模型,但我猜它也可以扩展到其他类型的模型。
图片取自 Packt
上图简要展示了从构建模型到使用 Tensorflow 服务将模型提供给端点的整个过程。为大多数类型的模型提供服务的最佳选择可能是在服务器上运行集中式模型,任何类型的设备(可能是桌面设备、移动设备或嵌入式设备)都可以从该服务器进行请求。然后服务器会为你做推理并返回预测。根据该预测,您可以将其呈现给任何设备。这种架构的一个很大的优点是,假设您有几个客户端访问集中在一个服务器上的端点。每个人都可以访问相同的模型版本,并可以无缝地享受更新的版本,而且通过添加负载平衡器,针对大量请求的扩展变得很容易。如果您将模型部署在每台客户机上,这将变得很困难,因此管理版本和发布新的更新变得很有挑战性。
现在让我们进入工作模式。
我们将训练一个神经网络模型来对服装上的图像进行分类(即 mnist 时尚数据集),保存训练好的基于 TensorFlow 的模型,并用 Tensorflow 服务它。虽然,更多的焦点将集中在服务模型部分。
您将在下面的 Git 资源库中找到所有代码。Star this repository关注我在 Medium, 我将在未来几周发布更多关于 Tensorflow 算法、ML 模型管道的更新,以及与 AWS 和 Azure 等云服务集成的示例。
如果你在 Google Collab 中运行这些代码,那就很容易了。
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/vjgpt/Tensorflow-Series/tree/master/tensorflow-serving)
import sys
# Confirm that we're using Python 3
assert sys.version_info.major is 3, 'Oops, not running Python 3.'import tensorflow as tf
from tensorflow import keras
import numpy as np
import os
import subprocess
在将数据输入模型之前对数据进行预处理。
# Model Version
VERSION = '00000123' fashion_mnist = keras.datasets.fashion_mnist
(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()# scale the values to 0.0 to 1.0
train_images = train_images / 255.0
test_images = test_images / 255.0# reshape for feeding into the model
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1)
test_images = test_images.reshape(test_images.shape[0], 28, 28, 1) print('\ntrain_images.shape: {}, of {}'.format(train_images.shape, train_images.dtype))
print('test_images.shape: {}, of {}'.format(test_images.shape, test_images.dtype))
基于时尚 MNIST 数据构建和训练 CNN 模型
model = keras.Sequential([
keras.layers.Conv2D(input_shape=(28,28,1), filters=8, kernel_size=3,
strides=2, activation='relu', name='Conv1'),
keras.layers.Flatten(),
keras.layers.Dense(10, activation=tf.nn.softmax, name='Softmax')
])
model.summary()epochs = 15model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
model.fit(train_images, train_labels, epochs=epochs) test_loss, test_acc = model.evaluate(test_images, test_labels)
print('\nTest accuracy: {}'.format(test_acc))
以 TensorFlow protobuf 格式保存训练好的模型。
saved_model_path = model.save(f"./tmp/{VERSION}", save_format='tf')
使用 SavedModel CLI 检查模型
SavedModel CLI 帮助您检查您的模型并检查模型的SignatureDef's
。它为您提供了模型训练所依据的输入张量数据类型和形状的详细信息。因此,您必须以相同的格式向模型提供输入,并获得由SignatureDef's
定义的输出。
import subprocesssubprocess.run([f"saved_model_cli show --dir ./tmp/{VERSION}/ --all"],shell=True)# Zipping the model as model.tar.gz
subprocess.run([f"tar cvfz model.tar.gz tmp/{VERSION}/"],shell=True)
我们将在本地机器上提供模型,所以我们需要压缩的模型文件和 JSON 文件,它们将被输入到模型中。
下载压缩的模型文件,这样您就可以在本地机器上部署它(假设您已经在 Google Collab 中运行了全部代码)。
from google.colab import filesfiles.download('model.tar.gz')
创建一个 JSON 文件,该文件将包含模型所需的输入SignatureDef's
,然后将被传递到模型端点。
import json# Wrap bitstring in JSON
data = json.dumps({"signature_name": "serving_default", "instances": test_images[0:3].tolist()})json_file = open('predict.json', 'w')
json_file.write(data)
json_file.close()files.download('predict.json')
设置 Docker 环境
从官方网站安装 Docker。
下载的快速链接:
- macOS 的 Docker
- 适用于 Windows 10 Pro 或更高版本的 Docker
让我们从最新的 Tensorflow 服务图像开始
docker pull tensorflow/serving
使用部署在 REST API 端点上的模型运行服务映像。
docker run -p 8501:8501 --mount type=bind,source=/path/to/the/unzipped/model/tmp/,target=/models/fashion_mnist -e MODEL_NAME=fashion_mnist -t tensorflow/serving
现在,您的 docker 容器正在运行 Tensorflow 服务模型服务器,绑定 REST API 端口 8501,并将模型从我们的主机映射到容器中预期的位置。还传递了一个环境变量,这对查询我们的模型很重要。
您可以使用预测 API 查询模型
curl -d ‘{"signature_name": "serving_default", "instances": [[[[0.0], [0.0]…………….[0.0]]]]}’ -X POST http://localhost:8501/v1/models/fashion_mnist:predict
或者您可以在本地机器上运行下面的脚本来获得预测
太棒了,现在您的模型不会在您的本地系统中丢失,但是您可以将您的模型部署到生产中,以便人们可以使用它。
您已经使用本地系统中的 Tensorflow 服务成功创建了一个端点。我将会发布更多的博客来运行相同的和其他几种类型的云服务模式。
如果这对你有帮助,就发微博,让人们部署他们的模型,因为这有点复杂,是机器学习生命周期的关键部分。
Star this repository关注我的 Medium, 我将在未来几周发布更多关于 Tensorflow 算法、ML 模型管道的更新,以及与 AWS 和 Azure 等云服务集成的示例。
在 GitHub 上注册你自己的个人资料,这是托管代码、管理项目和构建软件的最佳地方…
github.com](https://github.com/vjgpt) [## 维杰·古普塔
Vijay Gupta 的最新推文(@vj_guptaaa)。机器学习运营工程师。我主要谈论#技术…
twitter.com](https://twitter.com/vj_guptaaa)
参考
https://www.tensorflow.org/guide/keras/save_and_serialize
https://www . tensor flow . org/tutorials/distribute/save _ and _ load
https://www.tensorflow.org/tfx/serving/api_rest
https://github.com/zalandoresearch/fashion-mnist
TensorFlow vs PyTorch —卷积神经网络(CNN)
CNN 在 TensorFlow 和 PyTorch 中对一个著名数据集的实现及结果比较
在我之前的文章中,我已经给出了一个 简单线性回归 在 TensorFlow 和 PyTorch 框架中的实现,并比较了它们的结果。在本文中,我们将使用两种框架在一个非常著名的时尚 MNIST 数据集上完成卷积神经网络(CNN)的应用,并比较结果。
起源—
让我们简要了解一下框架及其历史。首先, PyTorch 是基于 Torch 库的开源机器学习库。PyTorch 最初是由脸书的人工智能研究实验室(FAIR)开发的。它是免费的开源软件。
另一方面, TensorFlow 由谷歌大脑团队开发,用于谷歌内部研究目的。它广泛用于机器学习应用,如神经网络。它也是免费的开源软件。
为了了解哪种框架更有效、更简单,我们将使用这两种框架构建一个 ConvNet,并对它们进行比较。对于张量流,我们将使用 Keras 库。
问题—
为了比较这两个框架,我们将使用著名的时尚-MNIST 数据集。它是由 60,000 个样本的训练集和 10,000 个样本的测试集组成的 Zalando 文章图像的数据集。每个例子是一个 28×28 的灰度图像,与来自 10 个类别的标签相关联,例如衬衫、包、凉鞋、外套、运动鞋等。我们将使用两种框架构建一个具有 LeNet 通用架构的 CNN 网络,并比较它们的结果。
步骤 1:导入库
第一步,我们将导入 TensorFlow 和 PyTorch 框架所需的库。
导入库
步骤 2:获取和拆分数据集
下一步,我们将获得时尚 MNIST 数据集。由于它是一个非常常见的数据集,两个框架都预加载了一些功能,使我们能够下载并使用数据集。
PyTorch
在 PyTorch 中,内置模块用于加载数据集。由于我们需要张量形式的数据,通过使用torchvision.transforms.ToTensor()
将数据转换成张量,并将其赋给变量transforms.
,然后使用torchvision.datasets
下载数据。数据相应地被分成训练数据集和测试数据集。
张量流
另一方面,与 PyTorch 版本相比,使用 TensorFlow 从 keras 库中获取数据更加简单。数据集从keras.datasets
加载,并相应地分成训练图像和测试图像。
步骤 3:加载数据
在这一步中,我们将把数据加载到训练集和测试集中。此外,我们还将查看图像在两个框架中是如何存储的。
PyTorch
在 PyTorch 中,图像存储在范围 0-1 中,因此图像以黑白显示。使用DataLoader
功能将图像加载到 train_loader 和 test_loader 中。此外,还会打印图像的标签。
张量流
另一方面,TensorFlow 存储 0–255 范围内的图像,因此同一图像中存在颜色渐变。TensorFlow 加载数据的方法更简单。
步骤 4:构建模型
在这一步,我们将建立 CNN 模型。为此,我们将构建标准的 LeNet-5 架构,这是一个非常简单的架构,建于 1998 年。下面给出的是 LeNet-5 的架构。
(来源)
PyTorch
在这里,我们使用 PyTorch 框架构建 LeNet-5 架构。它具有 2 个卷积层,每个卷积层之后是平均池层、2 个全连接层和具有 10 个类别的最终输出分类器层,因为最终输出具有 10 个类别的时尚项目。
在这种情况下,通过创建一个特殊的类“NeuralNet”来构建模型,其中我们必须使用 init 方法来定义网络/模型的每一层。将此函数‘forward’调用很重要,因为这将覆盖nn.Module
中的基本 forward 函数,并允许所有nn.Module
功能正常工作。
张量流
另一方面,在 TensorFlow 中构建相同的模型不需要创建类,即使我们使用了Sequential()
方法。我们发现,使用 TensorFlow 框架构建相同的模型比直接在 PyTorch 框架中构建相同的模型要简单明了得多。
步骤 5:可视化模型
在这一步中,我们将编译上一步中构建的模型,并设置优化器和损失函数。为此,在两个框架中,我们将使用 Adam 优化器和 分类交叉熵 损失函数,因为数据集中有 2 个以上的类。
PyTorch
在 PyTotch 中,我们首先创建一个在最后一步中构建的' NeuralNet '模型的实例作为modelpy
。然后,我们将优化器和损失函数分配给相应的变量。然后我们通过打印modelpy
来可视化模型。
张量流
另一方面,在 TensorFlow 框架中,我们使用了compile()
函数,在该函数中,我们将如上所述定义优化器和损失函数。然后我们使用summary()
来查看上面构建的 LeNet-5 架构的层。
步骤 6:训练模型
在这一步中,我们根据训练集中的数据部署为训练而构建的模型。对于这两个框架,我们将为 30 个时期训练模型,并查看结果。
PyTorch
对于 PyTorch,我们必须单独计算所有步骤,包括损失函数、优化器、反向传播和更新循环 的 中的每一步。
张量流
在 TensorFlow 中,时尚 MNIST 是一个二维数组,我们必须将训练数据集整形为张量,并将标签从整数转换为二进制类矩阵。使用fit()
函数对模型进行训练,该函数由定义的历元数组成。
第七步:比较结果
在最后一步,我们将比较使用测试集上的框架构建的两个模型的结果。
PyTorch
在 PyTorch 中,输出预测由outputs = modelpy(images)
预测。然后计算并显示测试集的准确度。
张量流
在 TensorFlow 中,predict()
函数用于预测测试数据集中图像的类别,并计算和显示准确度。
结果
测试集由 1000 幅图像组成,基于 PyTorch 和 TensorFlow 框架构建的模型用于预测测试集图像的类别。
*>>Test Accuracy of the model on the 10000 test images: **89%** with PyTorch>>Test Accuracy of the model on the 10000 test images: **91.34%** with TensorFlow*
从结果中我们看到 TensorFlow 模型的测试集准确率为 91.34% ,比 PyTorch 模型的 89% 略高。
结论
从上面的比较中,我们能够理解使用当今深度学习中使用的两个最流行的框架来构建 CNN 模型。
在我看来,如果你是一个初学者,完全不知道使用哪个框架,我建议你从 TensorFlow 框架开始,因为它更简单,更容易理解。另一方面,PyTorch 更快、更动态,但需要对 Python API 有一点了解。
我附上了我的 GitHub 库,你可以在那里找到比较的完整代码。
* [## MK-gurucharan/tensor flow-vs-py torch-CNN
GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码、管理项目和构建…
github.com](https://github.com/mk-gurucharan/TensorFlow-vs-PyTorch-CNN)
请查看我以前的文章,在这篇文章中,我给出了构建简单线性回归模型的两种框架的比较。
[## TensorFlow 与 PyTorch —线性回归
TensorFlow 和 PyTorch 框架中线性回归的实现及其结果的比较
towardsdatascience.com](/tensorflow-vs-pytorch-linear-regression-e07781a83525)
我真的希望我能够很好地比较今天深度学习中使用的两个最流行的框架。在我的下一篇文章中与大家见面。到那时,快乐的机器学习!*
TensorFlow 与 PyTorch —线性回归
TensorFlow 和 PyTorch 框架中线性回归的实现及其结果的比较
如果你是深度学习和神经网络的新手,那么你一定遇到过术语“ TensorFlow ”和“ PyTorch ”。这是在数据科学领域使用的两个流行的深度学习框架。
在本练习中,我将向您展示使用两种框架实现最简单的 神经网络 ( 线性回归 )并比较它们的结果。
起源-
PyTorch 是基于 Torch 库的开源机器学习库。PyTorch 最初是由脸书的人工智能研究实验室(FAIR)开发的。这是一个免费的开源软件。
另一方面, TensorFlow 由谷歌大脑团队开发,用于谷歌内部研究目的。它广泛用于机器学习应用,如神经网络。它也是一个免费的开源软件。
TensorFlow vs PyTorch(图片来自来源
比较两个框架最有效的方法是同时使用它们并分析它们的结果来解决同一个问题。在本练习中,我们将使用 TensorFlow 和 PyTorch 框架执行 线性回归 ,并比较它们的结果。
问题-
在这个练习中,我们将使用一个非常简单的例子。这里给我们一个数字数组, x= [-1.0,0.0,1.0,2.0,3.0,4.0] 和 y= [-3.0,-1.0,1.0,3.0,5.0,7.0] 。这里用到的公式是 y = 2*x -1 ,这是一个线性回归。
x= [-1.0, 0.0, 1.0, 2.0, 3.0, 4.0]
y= [-3.0, -1.0, 1.0, 3.0, 5.0, 7.0]y = 2*x -1
在使用这两个框架训练线性回归模型时,我们将为 x=10 输入一个新值,并查看模型对 y 的预测。
张量流框架-
首先,我们将通过张量流框架。
张量流—线性回归
这是使用 keras 库用 TensorFlow 执行线性回归的代码。让我们仔细检查上面程序中的每个代码块。
第一步,我们将导入库。
import tensorflow as tf
import numpy as np
from tensorflow import keras
在下一步中,我们使用序列、设计我们的模型,它是层的线性堆栈。在这个模型中,我们只使用了一层(神经元)。
model = tf.keras.Sequential([keras.layers.Dense(units=1, input_shape=[1])])
第二步,我们定义我们的优化器和损失函数来训练我们的神经网络模型。在此,我们使用随机梯度下降( SDG )优化器和均方误差( MSE )作为我们的损失函数。
model.compile(optimizer='sgd', loss='mean_squared_error')
这一步之后,我们用两个变量"【xs】"和" ys "初始化我们的数字。
xs = np.array([-1.0, 0.0, 1.0, 2.0, 3.0, 4.0], dtype=float)
ys = np.array([-3.0, -1.0, 1.0, 3.0, 5.0, 7.0], dtype=float)
在最后一步,我们用变量“xs”和“ys”来拟合我们的模型。我们用 500 个历元 训练模型。
model.fit(xs, ys, epochs=500)>>Epoch 500/500
1/1 [==============================] - 0s 1ms/step - loss: 5.1584e-05
在这最后一步,我们预测新的 xs = 10 的值。根据公式,y=2*x-1,对于 xs=10,我们得到一个值 19 。现在让我们看看我们的模型使用 TensorFlow 框架预测创造了什么价值。
print(model.predict([10.0]))>>[[**18.979048**]]
我们看到,使用 TensorFlow 框架设计的线性回归模型,我们得到了接近 19 的值。
PyTorch 框架
现在让我们看看使用 PyTorch 框架设计的线性回归模型。
PyTorch —线性回归
与 TensorFlow 模型相比,线性回归的 PyTorch 应用确实大且复杂。让我们分析模型的每一步。
第一步,我们导入设计线性回归模型所需的库。
import torch
from torch.autograd import Variable
在下一步中,我们将初始化与 TensorFlow 模型中定义的“【xs】”和“ ys ”相同的值。对于 PyTorch 应用程序,我们使用适当的函数将列表转换为张量。
**xs = [[-1.0], [0.0], [1.0], [2.0], [3.0], [4.0]]
ys = [[-3.0], [-1.0], [1.0], [3.0], [5.0], [7.0]]
xs = Variable(torch.Tensor(xs))
ys = Variable(torch.Tensor(ys))**
在这之后,我们定义一个类' LinearRegressionModel ',我们将使用它来定义我们的模型。由于这是一个具有 1 个输入和 1 个输出的简单线性回归,我们使用输入和输出维度都等于 1 的线性模型。最后,我们使用上面定义的类创建一个“ 模型 ”。
****class** **LinearRegressionModel**(torch.nn.Module):
**def** __init__(self):
super(LinearRegressionModel, self).__init__()
self.linear = torch.nn.Linear(1, 1) *# One in and one out*
**def** forward(self, x):
y_pred = self.linear(x)
**return** y_pred
model = LinearRegressionModel()**
接下来,我们选择优化器和损失标准。我们选择了与 TensorFlow 应用程序相同的函数,即优化器的 SDG 函数和损失函数的 MSE 。此外,我们任意地将学习率固定为 0.01。
**criterion = torch.nn.MSELoss(size_average = **False**)
optimizer = torch.optim.SGD(model.parameters(), lr = 0.01)**
我们现在到达我们的训练步骤。在这一阶段,我们为 500 迭代执行三项任务,因为我们将历元值设置为 500。
- 通过传递数据并预测每个 xs 的 ys 值,执行正向传递。
- 使用 MSE 损失函数计算损失。
- 将所有梯度重置为 0,执行反向传播,最终更新权重。
****for** epoch **in** range(500):
*# Forward pass: Compute predicted y by passing*
*# x to the model*
pred_y = model(xs)
*# Compute and print loss*
loss = criterion(pred_y, ys)
*# Zero gradients, perform a backward pass,*
*# and update the weights.*
optimizer.zero_grad()
loss.backward()
optimizer.step()
print('epoch **{}**, loss **{}**'.format(epoch, loss.item()))>>epoch 499, loss 5.151434834260726e-13**
最后,我们使用这个模型预测新的 xs=10 的值。如前所述,我们必须得到一个接近 19 的值。现在让我们看看 PyTorch 模型的输出。
**new_var = Variable(torch.Tensor([[10.0]]))
pred_y = model(new_var)
print(model(new_var).item())>>**18.999998092651367****
我们看到使用 PyTorch 框架构建的线性回归模型也给出了接近 19 的值。
比较-
在可视化从 TensorFlow 和 PyTorch 模型获得的结果时,我们看到 TensorFlow 模型给我们的结果是 18.979048 ,而 PyTorch 模型给我们的结果是18.998092651367。****
张量流模型— 18.979048
PyTorch 型号—18.99998092651367
可以清楚地看到,使用 PyTorch 框架构建的非常简单的神经网络比使用 TensorFlow 框架构建的模型具有更高的精确度。然而,PyTorch 模型本质上更复杂,初学者很难理解。
如果你对深度学习和神经网络完全陌生,我会建议你们从 TensorFlow 框架开始,然后在获得前者的经验后再转向 PyTorch 框架。
我附上了代码的详细版本。ipynb ),供您参考。
** [## MK-gurucharan/tensor flow-vs-py torch-线性回归-
这个库包含使用 TensorFlow 对 Python 中的数组执行线性回归的基本代码…
github.com](https://github.com/mk-gurucharan/TensorFlow-vs-PyTorch-Linear-Regression-/)
我希望我能够使用今天在深度学习中使用的两个最流行的框架来解释和演示简单神经网络(线性回归模型)的实现。到那时,快乐的机器学习!**
TensorFlow vs PyTorch:战斗仍在继续
本周,脸书宣布 PyTorch 1.5 的发布
照片由 @evanthewise 在 Unsplash 上拍摄
上周我们回答了问题 哪个是编程深度学习网络的最佳框架? 。而今天呢?脸书发布了新的 PyTorch 1.5,其中包括几个基于脸书和 AWS 合作的项目?
领先的深度学习框架
张量流
如今有许多可用的深度学习框架,然而, TensorFlow (由谷歌开发并于 2015 年 11 月作为开源发布)是目前业界使用最多的。
几周前 TensorFlow 发布了 TensorFlow 2.2 的发布候选。在这个候选版本中,我将重点介绍已宣布的性能改进和帮助测量性能的新工具的引入,如新的性能分析器。他们还增加了 TensorFlow 生态系统的兼容性,包括像 TensorFlow Extended 这样的关键库。
PyTorch
三年前出现了 PyTorch 的第一个版本,毫无疑问,它正在获得巨大的发展势头。PyTorch 最初由脸书孵化,作为快速实验和原型制作的理想灵活框架,迅速赢得了声誉,在深度学习社区中赢得了成千上万的粉丝。例如,我的研究团队中的博士生选择使用 PyTorch,因为它很简单。它允许他们编写具有本机外观的 Python 代码,并且仍然可以获得良好框架的所有好处,如自动微分和内置优化。
深度学习框架领域的明确领导者现在是谷歌开发的 TensorFlow 和脸书开发的 PyTorch,他们正在从使用量、份额和势头上拉开市场的距离。
模型部署
然而,构建和训练模型只是故事的一半。在生产中部署和管理模型通常也是一个困难的部分,例如,构建定制的预测 API 并扩展它们。
处理模型部署过程的一种方法是使用一个模型服务器,以便于加载一个或多个模型,自动创建一个由可伸缩 web 服务器支持的预测 API。到目前为止,生产环境中的可部署性仍然是 TensorFlow 的强项,而服务于的 TensorFlow 是最受欢迎的模型服务器。
正如我们已经说过的,本周脸书发布了 PyTorch 1.5。新版本的重点是提供工具和框架,使 PyTorch 工作流可以投入生产。这篇文章中最引人注目的方面是 AWS 和脸书在项目 TorchServe 中的合作,这是 PyTorch 的开源模型服务器。
据 AWS 博客报道,一些客户已经享受到了 TorchServe 的好处。丰田研究所高级开发公司正在丰田汽车公司开发自动驾驶软件。或者是 Matroid ,一家检测视频片段中的物体和事件的计算机视觉软件制造商。
随着 PyTorch 不可阻挡的崛起,TensorFlow 在深度学习方面的主导地位可能正在减弱。
PyTorch 1.5 版本
显然,这个新的 PyTorch 版本包含了更多的特性。亮点是 torch_xla、torchaudio、torchvision、torchtext 的更新包以及与 TorchElastic 集成的新库。我来简单总结一下:
- TorchElastic 是一个用于大规模训练大规模深度神经网络的库,具有动态适应服务器可用性的能力。在这个版本中,AWS 和脸书合作扩展 TorchElastic 的功能,将它与 Kubernetes 集成在一起,成为 Kubernetes 的 TorchElastic 控制器。要了解更多信息,请参见火炬信贷回购。
- torch_xla 是使用 XLA 线性代数编译器在云 TPUs 和云 TPU Pods 上加速 PyTorch 深度学习框架的 Python 包。torch_xla 旨在让 PyTorch 用户能够在云 TPU 上做他们在 GPU 上能做的一切,同时最大限度地减少用户体验的变化。完整的文档和教程可以在这里和这里找到。
- torchvision 0.6 版本包括对数据集、模型和大量错误修复的更新。完整的文档可以在这里找到。
- torchaudio 0.5 版本包括新的变换、泛函和数据集。参见这里的发布完整说明。
- torchtext 0.6 版本包括许多错误修复,对文档的改进,根据用户的反馈,数据集抽象目前也正在重新设计。完整的文档可以在这里找到。
- 这个版本包含了重要的核心特性,比如对 C++前端的重大更新,或者用于模型并行训练的分布式 RPC 框架的稳定版本。该版本还有一个 API,允许创建定制的 C++类。你可以在这里找到详细的发行说明。
PyTorch 1.5 版本暗示,AWS-脸书的合作可能是使 AWS 成为运行 PyTorch 程序的首选云运行时的第一步。
综上
虽然 PyTorch 在脸书(和 AWS)的帮助下在市场上获得了发展势头,但 TensorFlow 继续在各个方面保持领先,例如已经推出的认证计划就是证明。
谷歌继续在加强其 TensorFlow 平台堆栈方面进行大量投资。但仍有待观察的是,脸书是否会继续以同样的节奏投资 PyTorch,以保持其至少与 TensorFlow 在功能上旗鼓相当。
展望未来,这些框架之间的功能差距将继续缩小,正如我们在之前的文章中已经讨论过的那样。
2021 年 5 月 29 日更新:本周我们看到了微软和脸书合作推出的 PyTorch 企业支持计划,为在 PyTorch 中构建生产应用的企业用户提供支持。作为计划的一部分,微软宣布在微软 Azure 上发布 PyTorch Enterprise。
深度学习网络编程的最佳框架是什么?
towardsdatascience.com](/tensorflow-or-pytorch-146f5397278a)
数据科学家的终端
学习你的第一个命令
简介
如果您还没有使用过命令行工具,那么是时候开始使用了。
本文介绍了让您开始这一旅程的第一批命令。
您将学习如何浏览文件夹和文件,并在不打开任何编辑软件或使用图形用户界面的情况下修改它们。
这将使你成为一个更有生产力和更好的数据科学家。
午餐你的终端窗口
如你所见,我把终端放在 Mac 的 Dock 中,所以我可以在那里吃午饭。
应用程序打开后,您应该会看到类似下面的终端窗口。它应该在当前用户空间中打开。我这里的用户叫‘konki’。
这是您将要输入命令的窗口!!!
** * 注意,我将在这里展示的命令对 MacOS 和其他 Unix 操作系统有效。如果您使用的是 Windows 命令提示符,这些命令中的大部分应该可以工作,但是其他的可能需要一些替换。
- pwd(打印工作目录)
第一个命令将显示您当前所在的目录。只需键入“pwd ”,如下所示:
konkis-MacBook-Air:~ konki$ pwd
/Users/konki
你可以看到我现在在'/Users/konki '目录中。
2。ls(列表)
这个命令列出了一个目录中所有文件。让我们看一看。
konkis-MacBook-Air:~ konki$ ls
Applications Downloads Music cd
Desktop Library Pictures seaborn-data
Documents Movies Public
你可以看到我有 11 个目录,没有文件(如果我有一些文件,你会看到一些扩展名的名称。例如 txt. or。py)
您可以使用带-a 参数的 ls 来显示隐藏文件:
konkis-MacBook-Air:~ konki$ ls -a
. .ipython Downloads
.. .jupyter Library
.CFUserTextEncoding .matplotlib Movies
.DS_Store .pgAdmin4.startup.log Music
.Trash .pgadmin Pictures
.astropy Applications Public
.bash_history Desktop cd
.bash_sessions Documents seaborn-data
看起来我有相当多的隐藏文件。那些是以点开头的(。).
还可以通过传递相对路径来检查当前不在的文件夹的内容。
ls Desktop/
上面的命令应该列出桌面目录的内容。
3。mkdir ( 制作目录)
该命令用于创建新目录。以下代码行在名为 test 的当前文件夹中创建一个新目录。
konkis-MacBook-Air:~ konki$ mkdir test
您可以使用' ls '命令来检查它是否已被创建。
4。光盘(更改目录)
该命令用于将目录更改为给定路径的目录。一旦你创建了“测试”目录,你应该能够进入它。
konkis-MacBook-Air:~ konki$ cd test/
konkis-MacBook-Air:test konki$ pwd
/Users/konki/test
您可以使用上面的“pwd”命令来确认您的位置。
“cd”命令的其他有用变体是“cd”(不带参数)和“cd~”。它们都将带您到主目录。
cd
cd~
同样,“cd”的常见用法是返回到父目录,这可以用两个点来完成。
cd ..
5。触摸
您可以用它来创建一个新文件。下面的代码创建 my_file.py .然后使用' ls '命令检查目录的内容。
konkis-MacBook-Air:test konki$ touch my_file.py
konkis-MacBook-Air:test konki$ ls
my_file.py
我们创建的文件只是一个空文件。py 扩展名。稍后我们可以添加一些 python 代码。
6。rm(删除)
我们可以用 remove 命令删除文件或目录。有了文件,用如下所示的文件名调用这个命令就足够了。
konkis-MacBook-Air:test konki$ rm my_file.py
konkis-MacBook-Air:test konki$ ls
konkis-MacBook-Air:test konki$
您总是可以确认文件是否是用上面的' ls '命令删除的。
如果您想删除整个目录,您必须添加-r 参数。这将使 remove 命令递归地工作,并进入所有子目录来删除它们的内容。
rm -r <directory_name>
7。mv(移动)
你可以用“mv”命令移动文件和目录。您只需要指定要移动的文件或文件夹名称以及新的所需位置(文件夹或文件名)。
mv <file_name> <new_location>
让我们创建一个新文件,并将其移动到父目录。
konkis-MacBook-Air:Downloads konki$ touch file.txt
konkis-MacBook-Air:Downloads konki$ mv file.txt ..
最后一行将 file.txt 移动到父目录。记住,两个点(..)是父目录的快捷方式。
8。cp(副本)
您也可以复制文件和文件夹,而不是移动它们。该命令的结构类似于 move 命令。您需要指定要复制的文件和目的地。
cp <file_name> <new_location>
让我们创建一个新文件,并将其复制到父目录。
konkis-MacBook-Air:Downloads konki$ touch file_to_copy.txt
konkis-MacBook-Air:Downloads konki$ cp file_to_copy.txt ..
现在,如果您使用' ls '命令,您将看到该文件仍然在它的原始位置。
如果您返回到带有“cd ..”的父文件夹再次使用“ls ”,您会看到“file_to_copy.txt”也在那里。它在没有删除原始文件的情况下被复制到了那里。
9。man(手动)
当您忘记如何使用上述任何命令时,此命令非常有用。它会调出特定命令的手册。
man <command>
您可以使用“pwd”命令进行测试。
man pwd
这将打开对“pwd”命令的描述,如下图所示。
您可以使用箭头向下滚动访问底部的文本,并键入“q”退出文本文件。
总结
我想这是我在浏览 Macbook 和处理项目时最常使用的命令列表。与使用图形用户界面相比,它节省了我大量的时间和精力。
从学习这九条开始,努力做到每天都使用命令行。
原载于 aboutdatablog.com: 数据科学家终端,2020 年 8 月 26 日。
PS:我正在 Medium 和上撰写深入浅出地解释基本数据科学概念的文章。你可以订阅我的* 邮件列表 *每次我写新文章都会收到通知。如果你还不是中等会员,你可以在这里加入。**
下面是一些你可能会喜欢的帖子
数据科学家的最佳生产力工具,如果您还没有使用它,您应该使用它…
towardsdatascience.com](/jupyter-notebook-autocompletion-f291008c66c) [## 9 大 Jupyter 笔记本扩展
改进笔记本电脑功能,提高您的工作效率
towardsdatascience.com](/top-9-jupyter-notebook-extensions-7a5d30269bc8) [## 作为一名有抱负的数据科学家,你应该关注的中型作家
我最喜欢的 10 个数据科学博主,让你的学习之旅更轻松。
towardsdatascience.com](/medium-writers-you-should-follow-as-an-aspiring-data-scientist-13d5a7e6c5dc)**
白蚁第一部分:对隐私难题的回应
没有人真正阅读的隐私政策的数据科学解决方案
为什么是隐私?
“如果你没什么好隐瞒的,就不该担心。”这种经常听到的论点是一种错误的尝试,试图证明一个人的隐私受到损害是合理的。此外,我认为正是像这样的旁观者逻辑使得强者逐渐侵犯你的权利,直到为时已晚。
在加州大学伯克利分校信息学院的数据科学硕士(MIDS)项目的最后一个学期,学生们的任务是构建一个最小可行产品(MVP)。进入研究生院的最后阶段,我对有机会使用我在过去一年中发展的数据科学技能来促进我对一项有价值的事业的理解感到乐观。我和我的团队(詹妮弗·帕特森、朱利安·佩尔茨纳和奥利·唐斯)选择追求的目标是互联网隐私。
我对隐私的探索是由我同时受雇于安全与技术研究所而激发的。在对数字平台如何影响人类认知以及如何反过来影响民主制度进行初步研究时,我发现自己一直在想,为什么人们如此信任在线平台。反过来,为什么要对网络论坛持怀疑态度?(免责声明:对我设计的产品/插件的任何提及都不以任何方式隶属于安全与技术研究所,或他们的任何项目或程序。)虽然我仍然无法完全回答这些问题,但我参与的顶点项目为我提供了一个解决这些问题的机会。
在全球化的新冠肺炎疫情中,我们花在网上的时间比以往任何时候都多。因此,数据收集和监测也在增加。关于接触者追踪和监视的权衡的辩论正在我们眼前展开。一方面,追踪接触者可能有助于减少病毒的传播。另一方面,在我们战胜疫情后,政府将如何使用同样的监控技术?我们真的相信政府会放弃使用这些先进工具的特权吗?谁敢说权力机构不会转而使用类似的技术来驱逐移民或识别和骚扰表达其第一修正案抗议权利的公民?
我 11 年级的历史老师安迪·欧文斯曾经说过,“特权永远不会自动放弃。”我认为在这个不确定的时代,这仍然是真理。关于在线隐私的讨论再恰当不过了。这不是一个新问题。有关隐私风险规避和隐私行为的更深入讨论,请阅读与我们在一起很安全!消费者隐私科学作者亚里沙·弗里克和玛蒂尔达·拉克。本文中提到了几个有趣的发现:
- 十分之六的美国成年人认为日常生活中不可能不收集他们的数据,81%的人认为潜在的风险大于好处。
- 全球 69%的人表示,如果某个品牌的数据使用太具侵犯性,他们会避免与该品牌做生意。
- 在英国、美国、法国和德国,78%的人表示他们会保护自己的财务信息,相比之下,只有 57%的人会保护自己的联系信息。
- 只有 9%的美国人在签署条款和条件(T & Cs)之前会阅读隐私政策,47%的人表示对围绕欧盟一般数据隐私法规的信息感到厌倦。
- 72%的美国人觉得他们在网上或手机上做的几乎所有或大部分事情都被广告商和科技公司追踪。
- 48% 关心隐私的消费者因为公司或服务提供商的数据政策和做法而转向他们。
- 14%的英国人愿意分享数据,如果他们能得到报酬的话。但这些回报也可能是间接的。来自约克大学的研究表明 54%的英国人愿意牺牲一些数据隐私来缩短封锁期。此外,如果一家公司在如何使用数据方面更加透明,73%的人会愿意分享数据。
我们通过自己的用户研究证实了这些统计数据,研究显示,50%的受访者表示他们从未阅读过网站的隐私政策,我们的受访者中没有人表示他们总是阅读政策。此外,当被问及是否对互联网负有责任时,67.2%的受访者回答“不”或“我不知道”这不仅向我们表明互联网用户没有感觉到他们的安全需求得到了满足,也向我们表明许多用户只是不知道他们的信息是否安全。
我们寻求解决的问题与隐私政策有关,理想情况下,隐私政策应该是让用户对一家公司的安全数据收集做法感到放心的地方。然而,这种情况很少发生。隐私政策的主要问题是它们太长,充满了技术术语和“法律术语”隐私政策通常是由寻求使公司免受诉讼的律师撰写的。它们很少是为了保护用户利益和促进他们的意识而写的。最近,从公共关系的角度来看,构建更加透明的隐私政策变得越来越流行。然而,这些都是边缘案例,隐私政策对于普通互联网用户来说仍然难以阅读、理解、总结和分析。事实是大部分人都不看,不知不觉就同意了。
白蚁
这就是我们推出白蚁的原因:这是一款免费的浏览器附加工具,使用网页抓取和 NLP 方法来自动化和扩展在线服务条款、条款和合同以及隐私政策的评估和评级。白蚁还为用户提供定制的网络卫生报告,并使用户能够跟踪他们的在线协议,所有这些都是实时的。我们的目标是优化用户界面、感知和可操作性。当隐私政策和条款和条件是不可读的,白蚁率和跟踪他们。当用户对他们的信息安全感到不确定时,白蚁赋予他们权力。当网站利用用户不阅读他们的政策,白蚁让用户知道,保护他们。点击这里查看我们的简短演示!
克里斯·里瓦斯的标志(@the_hidden_talents)
有一些工具和服务,都服务于自己的领域。他们每个人在解决各自的问题空间方面都做得很好。总的来说,风景仍然不完美。在这些不同的努力之间缺乏一个哲学上的桥梁,留下了进一步连接这些点的机会。以下是我们当前隐私问题的一些现有解决方案及其各自的缺点:
Mozilla Firefox
Mozilla Firefox 是一款开源的网络浏览器,具有展示的 隐私第一 的精神,是市场份额第二大的浏览器。2018 年,Mozilla 基金会和 Mozilla 公司的总收入为 4.36 亿美元。他们目前占据了浏览器市场 7.58%的份额。相比之下,谷歌 Chrome 是最大的浏览器,目前占浏览器市场的 68.81%。Mozilla Firefox 培养了一家在隐私问题上言行一致的公司。
达克达克戈
DuckDuckGo 是一个搜索引擎,它会在可能的情况下自动加密用户与网站的连接(这样你的个人信息就不会被收集或泄露),阻止广告追踪器,并警告用户不良的隐私做法。按市场份额计算,他们是第六大搜索引擎,拥有超过 5000 万用户。2018 年,他们的估值在1000 万至 5000 万美元之间,目前他们代表着 1.36%的搜索引擎市场。相比之下,谷歌是最大的搜索引擎,目前占据 70.37%的市场份额。DuckDuckGo 代表了最全面、最成功的解决方案。
来源:https://duckduckgo.com/traffic
服务条款;没读过(ToS;博士)
服务条款;没读过(ToS;DR) 的成立是为了众包在线隐私政策分析和隐私等级。它值得被认为是对用户进行在线隐私教育的最佳尝试。对 ToS 的公正批评;然而,DR 认为,通过众包政策分析和评级,他们的网站得分缺乏一致性。此外,他们的隐私主题索引跨越了 24 个类别,太多了以至于没有帮助和用户友好。此外,他们的分析是用户生成的,这意味着由人类手动进行,导致站点分析的范围有限。
来源:https://tosdr.org/topics.html#topics
波利西斯
Polisis 是一个 chrome 扩展,为用户提供人工智能驱动的任何隐私政策摘要。作为一种可视化隐私政策的独特方式,Polisis 利用深度学习和人工智能来教育用户公司正在收集关于你的哪些数据,它正在分享什么,等等。你不必阅读完整的隐私政策和所有的法律术语来理解你在注册什么。用 Polisis 的话说,他们认为他们的产品“实现了对自然语言隐私政策的可扩展、动态和多维查询。Polisis 的核心是一个以隐私为中心的语言模型,由 13 万个隐私策略和一个新颖的神经网络分类器层次结构构建而成,既考虑了隐私实践的高级方面,也考虑了隐私实践的精细细节。”据 Chrome 扩展商店称,Polisis 拥有 1000 多名用户。他们将其隐私类别分为以下 10 类:1)第一方收集,2)第三方收集,3)访问、编辑、删除,4)数据保留,5)数据安全,6)特定受众,7)不跟踪,8)政策变化,9)其他,以及 10)选择控制。尽管 Polisis 是一个很好的工具,它采用了非常有趣的数据可视化,但我的经验是它很慢,而且不太用户友好。
特权者
Privee 是一个概念验证浏览器扩展,旨在自动分析隐私政策。他们夸口说他们已经分析了 38522 个网站,而且还在继续。Privee 自 2014 年以来一直没有被开发,在谷歌 chrome 扩展商店上只有 42 个用户。为了训练他们的模型,他们依靠 ToS;灾难恢复策略摘录注释。为了开发和测试他们的模型,他们使用正则表达式从新网站中提取适当的政策摘录。它们涵盖的以下六个隐私类别包括:1)收集、2)加密、3)广告跟踪、4)有限保留、5)剖析和 6)广告披露。
DuckDuckGo 等工具的创新,以及众包努力的相对成功(包括财务和分析服务形式)标志着一种趋势的开始。越来越多的公司要求在设计时考虑隐私原则。在缺乏足够广泛的监管和合规的情况下,学术界、私营部门和公民社会已经表明,他们愿意一点一点地解决隐私问题。尽管如此,还需要在尚未充分覆盖的领域取得更多进展。
作者图片
这篇博客是五篇博文的开始。在“白蚁第二部分:模型和特性选择”中,我讨论了我们团队面临的困境,以及我们不可避免要做出的决定。访问超链接中的零件三个、四个、五个。
特里纳尔伯特:量子化与蒸馏相遇
华为对 BERTology 的贡献
建造像伯特和 GPT-3 这样越来越大的模型的趋势一直伴随着一种互补的努力,即以很少或没有精度成本来减小它们的尺寸。有效的模型要么通过蒸馏(预训练蒸馏、蒸馏伯特、移动伯特、 TinyBERT )、量化( Q-BERT 、 Q8BERT )或者参数修剪来建立。
9 月 27 日,华为推出了 TernaryBERT ,这是一种利用蒸馏和量化来实现与原始 BERT 模型相当的精度的模型,尺寸缩小了约 15 倍。TernaryBERT 真正值得注意的是,它的权重是三进制的,即具有三个值之一:-1、0 或 1(因此只能存储在两位中)。
TernaryBERT 巧妙地将现有的量化和提炼技术结合在一起。这篇论文大量引用了以前的工作,因此相当密集。本文的目标是提供一个自包含的演练,并在需要时提供额外的上下文。
马库斯·斯皮斯克在 Unsplash 上拍摄的照片
量化
量化是减少用于表示单个标量参数的位数的过程。
当成功时,量化相对容易,因为它允许模型设计者保持原始模型的体系结构和配置不变:通过从 32 位切换到 8 位参数表示,可以实现 4 倍的大小缩减,而不必重新访问像层数或隐藏大小这样的设置。量化一般把实数值映射到整数值,可以有效得多的相加相乘。
量化方案
量化方案是确定一个实值 r 如何映射到一个可以由目标比特数(对于 8 比特:-128 到 127 包括在内,或者 0 到 255 不包括在内)表示的整数(或量子) q 的算法。最常见的是,量化方案是线性的。 r 和 q 之间的关系可以用比例因子 S 和零点 Z 来表示:
线性量化:用比例因子 S 和零点 Z 表示的真实值 r 与其量化值 q 之间的线性关系。方程式(1)来自 Jacob 等人[Q1]
在实值模型参数均匀分布在区间[-1,1]且量程从-127 到 127(暂时忽略-128 桶)的最简单情况下, r = 1/127 * q. 换句话说,比例因子 S 为 1/127 ,零点 Z 为 0 。当 r 不均匀分布时(假设其值主要为负值),零点会相应移动。例如,当 Z=10 时,我们将 10 个量子重新分配给负值,从而提高大多数 r 值所在区域的精度。
TernaryBERT 对其权重和激活都应用了线性量化(下面将详细介绍)。但就上下文而言,你应该知道还有其他方案。例如,在神经网络[Q2]的上下文中介绍了三值化(即量化为-1,0 和 1)的论文提出了一种随机算法,用于将实数 r ∈ [-1,1]转换为量化值 q ∈ {-1,0,1}:
- 如果 r ∈ (0,1)q =1 以概率 r 和 q =0 以概率 1-r.
- 如果 r ∈ [-1,0], q =-1 以概率 -r , q =0 以概率 1+r.
量子化应该在什么时候发生?
应用量化的最方便的时间是后训练:在用 32 位实值参数训练模型之后,应用标准量化方案之一,然后使用量化的模型进行有效的推断。然而,在实践中,这种天真的方法往往会导致精度大幅下降,尤其是针对超低精度(2 或 4 位)时。即使我们在这种训练后量化之后执行额外的微调步骤,结果仍然不能令人满意;量化降低了模型参数及其梯度的分辨率(即可以表示多少不同的值),从而阻碍了学习过程。
为了解决这个问题,Jacob 等人【Q1】提出了用模拟量子化进行训练:在正向传递过程中,模型的行为就好像它已经被量子化了一样。因此,损失和梯度是相对于这种位约束模型来计算的,但是反向传递照常在全精度权重上发生。这鼓励模型在量化正向传递期间表现良好(这是在推断时发生的),同时继续利用较长位参数和梯度的更好的表示能力。Q8BERT [Q3]使用这种技术(也称为量化感知训练)将原始的 BERT 模型从 32 位减少到 8 位整数表示。TernaryBERT 采用了类似的策略。
三元伯特中的权重三元化
TernaryBERT 通过如上所述的线性量化方案将其 32 位实值权重转换成具有来自集合{-1,0,1}的值的 2 位三进制表示。当零点固定为 Z=0 时,比例因子 S > 0 (从这里开始用 α 表示)与模型参数一起被学习。
由 Florencia Viadana 在 Unsplash 上拍摄的照片
不用说,将参数从 32 位降级到 2 位会带来精度的巨大损失。为了恢复模型的一些失去的表达能力,通常的做法是使用多个比例因子 αᵢ (而不是用于整个网络的单个 α ),一个用于参数(矩阵、向量、核、层等)的每个自然分组 i 。).例如,Rastegari 等人[Q4]在其卷积神经网络的每一层上为每个滤波器使用了单独的比例因子。类似地,TernaryBERT 为 BERT 的每个变换层添加一个 αᵢ ,并为令牌嵌入矩阵添加单独的每行缩放因子。
那么这些比例因子是如何习得的呢?如上所述,比例因子 αᵢ、全精度权重 w 和量化权重 b 都是在训练过程中学习的。TernaryBERT 比较了两种用于近似这些参数的现有方法:三元权重网络(TWN)【Q5】和损失感知三元化(LAT)【Q6】。虽然这两者具有看似不同的公式,但它们都归结为最小化量化前向传递的预测损失,并具有附加约束,其中在每个训练步骤期间,鼓励量化权重αb保持接近全精度权重 w 。
如果你时间不够,你可以直接跳到“激活量化”部分,确信 TWN 和拉特在 GLUE 基准(主要包含分类任务)和 SQuAD (一个流行的问题回答数据集)上得分相当。
选项 1: TWN(三元加权网络)
三进制权重网络 (TWN)将问题公式化为最小化全精度和量化参数之间的距离:
“基于近似的量子化”。在训练步骤 t 期间,选择量化参数 b 和比例因子 α ,使得它们最小化到全精度权重 w 的距离。这是来自三元伯特的方程式(5)。
因为这种最小化是在每个训练步骤上执行的,所以有效的实现是至关重要的。幸运的是,有一个近似的解析解,所以计算 b 和 α 就像应用一个依赖于 w 的公式一样简单(为简单起见,本文不包括)。
选项#2: LAT(损失感知三值化)
另一种方法,损失感知三值化 (LAT),是直接最小化相对于量化权重计算的损失;这个表达式完全避开了全精度参数(注意下面没有 w ):
“有损量化”。在整个训练过程中,选择量化参数 b 和比例因子 α ,使得它们最小化训练数据上的损失 L (关于 L 的定义,参见蒸馏部分)。来自三元贝的方程式(6)。
首先,上面的表达式应该看起来有些可疑:我们已经确定,以低位表示训练模型会受到精度损失的阻碍。当然,全精度权重 w 需要以某种方式参与进来。事实证明,该表达式可以根据每次迭代最小化子问题来重新表述,其中量化权重再次被鼓励接近全精度权重,但是以还考虑当前时间步长的损失的方式:
每迭代损失感知量化。在训练步骤 t 期间,选择量化参数 b 和比例因子 α ,使得它们最小化到全精度权重 w 的特定距离。这是来自三元伯特的方程式(7)。注意与等式(5)的相似性。这里的距离是以 v 表示的,即等式(6)中损耗 L 的对角近似值。
该表达式与等式(5)非常相似,不同之处在于它包括 v ,这是等式(6)中的损失的统计。与等式(5)类似,它有一个近似的解析解,是 w 的函数,可以有效地计算(同样,为了简单起见,不包括在本文中)。
激活量子化
在权重量化之后,该模型可以被描述为被分组为逻辑单元(例如矩阵)的一组三元权重,它们中的每一个都具有其自己的实值缩放因子 αᵢ 。因此,流经网络的值(层的输入和输出),也称为激活、是实值。为了加速矩阵乘法,激活也可以被量化。然而,正如在以前的工作[Q7]中提到的,激活对量子化更敏感;这很可能是为什么 TernaryBERT 决定将激活量化到 8 位而不是 2 位的原因。
西蒙·哈默在 Unsplash 上拍摄的照片
基于对变压器激活倾向于负向多于正向的观察,TernaryBERT 的作者选择了一种非对称量化算法,或者,根据高于r = S(q-Z)*的线性量化表达式,零点 Z 不固定为 0,而是 r 的最小和最大可能值的中点*
蒸馏
从最初天真的提议到执行训练后量化,我们已经走了很长的路。我们确定量化需要成为训练的一部分,需要多个比例因子来衰减由从 32 位降级到 2 位引起的精度损失,并探索了使量化权重接近全精度权重的两种不同方法。但是 TernaryBERT 告诉我们,通过利用机器学习工具箱中的另一种技术,我们可以做得更好。
提炼(或称知识提炼)是一个大而精确的模型(老师)将其知识转移到一个代表性力量较小的模型(学生)的过程。
换句话说,蒸馏是一个两步走的过程:1)训练一个大的老师使用金标签,2)训练一个小的学生使用老师制作的标签,也称为软标签。辛顿等人[D1]解释说,蒸馏优于标准训练,因为软标签携带额外的信息,或暗知识。例如,在句子“我喜欢散步”中,考虑用单词“散步”的正确词性来标记它。形式为 p(名词)=0.9 的软标签比形式为 p(名词)=1.0 的硬标签更能提供信息,硬标签未能捕捉到“walk”在其他上下文中可能是动词的事实。TernaryBERT 在微调过程中使用相同的技术,从一个大得多的教师产生的软分布中学习:
蒸馏损失第 1 部分:学生和教师预测之间的软交叉熵。来源:特里纳尔伯特。
诸如 FitNets [D2]等更精细的提炼公式鼓励教师和学生的内部表征之间的直接一致。TernaryBERT 也这样做,将完全精确的学生的隐藏层拉近教师的隐藏层,并鼓励两个转换网络的注意力分数相似:
蒸馏损失第 2 部分:学生/教师隐藏权重和注意力分数之间的均方误差。来源:三月伯特。
端到端:蒸馏感知三值化
蒸馏感知三值化。图 2 来自三月伯特。
TernaryBERT 将来自量化和提炼的既定技术放在一个单一的端到端配方下,用于训练三值化模型。在每个训练步骤中:
- 完全精确的学生是三位一体的。实际上,这相当于模拟量化,Jacob 等人[Q1]介绍的方法:正向传递在低表示中执行,以模拟在推断期间将真正发生的事情。使用前面描述的两种方法之一(TWN 或 LAT)来执行三值化。
- 蒸馏损失( L_pred + L_trm 根据量化模型所做的预测进行计算,但是梯度更新(即反向传递)应用于全精度参数。
在训练结束时,量化模型已准备好进行推断,无需进一步调整。
量化参考
- [Q1]雅各布等人,用于高效整数算术推理的神经网络的量化和训练 (2017)
- [Q2]林等,少乘法神经网络 (2016)
- [Q3] Zafrir 等人, Q8BERT:量化的 8 位 BERT (2019)
- [Q4] 拉斯特加里等人,XNOR 网络:使用二进制卷积神经网络的图像网络分类 (2016)
- 李等,三元权重网络 (2016)
- [Q6]侯,郭,深度网络的损失感知权重量化 (2018)
- [Q7]周等,DoReFa-NET:用低位宽梯度训练低位宽卷积神经网络(2018)
蒸馏参考
- [D1]辛顿等,在神经网络中提取知识 (2015)
- [D2]罗梅罗等人, FitNets:提示薄深网 (2014)
机器人可能会接管你的公司,但还不是时候
意见
利用最新的科学来辨别炒作和真相
摄影爱好在 Unsplash 上
这里有很多关于奇点事件和人工智能接管世界的讨论。他们自己的电脑会很快取代杰夫·贝索斯或马克·扎克伯格吗?
由于我是一名为经理开发卫星导航工具的首席执行官,并且卫星导航是走向无人驾驶汽车的第一步,这些天我经常被问及天网问题——科学家会让公司由机器人管理吗?
答案是绝对没有。原因如下。
奇点事件:炒作
当然,人工智能在过去的 30 年里突飞猛进。然而,我们甚至还没有接近机器可以复制或超越人类推理的奇点事件。
从本质上来说,这个新的数据科学术语是一个对大量数据处理活动的高贵称呼,这些活动大多是很久以前 发明的 。他们最近获得了新的生命,因为应用了大大增强的技术设备:更多的数据,更强的处理能力,更合理的结果,更便宜的价格。
随着存储和处理数据的成本下降,收集的数据量上升:非常简单的供求定律,或者你可以称之为数据的价格弹性。价格下降,交易量上升。这意味着 新数据的 值比旧数据小。纯经济学。然后,有人将不得不对所有这些东西做些什么。进入数据科学。
数据科学大多是老科学;有人记得专家系统吗?尽管大肆宣传,这项科学即使在今天也有严重的技术局限性:
- 仅自下而上,从数据向上。例如,图像被扫描成数百万或数十亿像素的集合,这些像素是数字的向量。低效:今天对某些问题有效,因为它很便宜,然而,它在学习上仍然慢得多,并且比使用人脑做任何其他事情都更昂贵。一个 3 岁的孩子能说出什么是香蕉当连续看到两张时,一个昂贵的系统需要 100 万张图像来训练自己,有 95%的信心,并且仍然会每 20 张图像犯一次错误。
- 只有电,缺少大脑的化学部分。神经网络是机器学习的核心,它们在复制大脑的电气部分方面有些成功:神经元和穿过它们的路径中交换的信号。尽管如此,对大脑中可能负责整体思维、情感和自上而下推理的化学部分的研究仍然很少:想想直觉。
最好的卫星导航系统甚至能驾驶你的车,但不能告诉你去哪里。
这是机器在结构上遗漏的大量功能。这正是人工智能与人类辩论的要点——机器做它们被编程做的事情,而人可以发挥创造力。
问正确的问题是人类的终极能力,只是遗憾的是,今天的学校正在教授如何回答问题,这正成为一种越来越不相关的技能。
这就是我们不必要地惊慌失措的原因吗?
优步、谷歌和特斯拉正在争论这个问题
目前,关于人工智能的辩论正在由自动驾驶汽车形成。随着优步、谷歌和特斯拉竞相推出市场上的第一辆无人驾驶汽车,人们认为这只是一个开端,并将很快导致机器运营一家公司,甚至是这个星球。
事情是这样的。
管理一家公司需要复杂的技能来驾驭模糊性和理解人类情感——这通常是“混乱的”和不可量化的。定义一个目标最终会改变市场,需要定义一个新的目标,一个叫做反身性的属性。
反身性指的是原因和结果之间的循环关系,尤其是在人类的信仰结构中。自反关系是双向的,在这种关系中,原因和结果都相互影响,但都不能被指定为原因或结果。
自动驾驶汽车只需要使用位置和速度等可测量的数据来读取物理事物——路况、其他汽车、行人。即使这样,也可能有程序员没有考虑到的“黑天鹅事件”,然而这些事件的数量是有限的,最终它们会被映射出来。
从结构上来说,一旦一辆车能开了,就完了;而管理行为将导致市场范式的转变,整个循环需要机器再次从头开始。
有趣的例子:认知失调
2012 年,在澳大利亚墨尔本,一辆运羊的卡车撞上了一座立交桥,导致数百只羊从下方的高速公路上倾泻而下。
虽然人类可以克服飞羊的认知失调,但你可以想象一个困惑的优步机器人说出那句古老的科幻栗子:不会计算!
这是一个意外事件的极端例子。笑一笑,解释一下为什么机器不会很快接管公司,可能会有所帮助。在管理的竞争游戏中,有许多绵羊在飞来飞去。
机器人企业:真相
当然,就像工业革命时期很多体力劳动岗位被机器取代一样,很多行政职能也可以转移到机器上。然而,更具创造性和高层次思维的角色将留在人类手中并得到加强。
使用的数据越多,就越需要领导力来指引方向。
这种人工智能/人类的相互作用一直是汽车制造商大众汽车公司近年来大量研究的主题。首席信息官马丁·霍夫曼(Martin Hoffman)解释了该公司如何将自动驾驶汽车的清单应用于“机器人企业”领域——在企业职能和流程中使用自适应算法。
一个例子
用一个实际的例子来总结:想象一下仓库重新订货过程的自动化,这是我个人非常熟悉的一个过程。今天,我们拥有可以让供应链自动运行的系统,除了策略仍然需要由人类来设定:
- 选择更多库存和更高成本的更好客户服务之间的权衡,还是选择投资更少但有缺货风险的更低库存?目标是未来几周的报道?
- 不存在最优水平,因为最佳答案是这取决于,决策的坐标要求将战略前景的变化转化为日常调整
- 机器决定了有效边界对于权衡来说,对于任何给定的目标覆盖周数,都只有一个最佳的仓储水平;人类必须选择周数。
大众汽车已经确定了 5 个人工智能控制级别,从人类做出所有商业决策的第 1 级(人工),到完全没有人类输入的第 5 级(全自动)。
一般来说,机器在大众汽车级别的 2 级(辅助)表现出色:机器提供建议,但人类做出最终决定。机器不会取代人类的判断,只会增强人类的判断,就像我们今天的手机每天都在增强我们的大脑一样。
快乐管理!
PS 我定期写关于 商业科学 。
测试你的数据,直到它受伤
理解大数据
数据测试故事
如果你从事分析工作,可以肯定你已经不止一次抱怨过数据质量问题。我们都有,痛苦是真实的!
作为一名数据工程师,数据及其质量是我最关心的。我敏锐地意识到良好的数据质量是多么重要——最近这个话题引起了一些热议,例如# 数据操作。有很多关于这个主题的文章:只需进入 DataKitchen 的 DataOps Medium 页面,或者如果你更喜欢 MLOps 这个术语,也有很多内容,比如来自 great expectations 博客的这篇关于 MLOps 中数据测试的博文。
还有更多——但是从我的经验来看,在实践中缺乏对系统数据测试和质量监控的采用。实际上,即使有数据测试,它也经常以一些断言被破坏到数据管道中而告终,污染了数据管道代码,并且没有创建任何数据质量问题的可见性。在这篇文章中,我想给我们的(正在进行的)数据测试之旅一些见解,以及为什么它会带来伤害!
数据质量
我看到的数据质量的第一个问题是,它是一个非常模糊的术语。如何定义数据质量或者哪些数据质量问题很重要的实际含义取决于您的用例以及您的数据类型。
一个常见的疑点是缺少数据/观察值,要么是因为记录不完整,要么是因为您管道中未被注意到的复制错误。企业数据存储中的上游作业发生了变化,数据中的行/观察值突然变少了。然后是错误的输入,也就是说,一个人在某个地方的字段中输入了一个明显错误或格式错误的数字。这样的例子不胜枚举。
好的,如果进入你系统的数据有问题,你输出的数据怎么办?即使你设法解决了这些问题,你能确保你发布的数据质量一流吗?另一个数据质量问题。
总而言之,在如何处理数据质量问题上,有许多事情需要考虑,还夹杂着许多噪音和不确定性。
你应该关心
如果您仍然认为您没有数据质量问题,因为您的代码通过了所有的自动检查,并且您模拟了所有的数据,甚至对您的样本数据进行了单元测试,那么您就错了!或者说,你现在可能是正确的,但最终当你的数据出现问题时(它们会出现),你很可能会在不知情的情况下使用并发布一些糟糕的数据。
如果你不测试你就不知道,就这么简单。所以现在就开始测试吧!而且一旦开始测试,就要测试到疼为止。如果您只是收集显示管道运行 X 行的数据指标,这样您就可以为您的自动化技能有多棒而沾沾自喜,这不会为您产生任何价值。或者反过来,套用丹尼尔·莫尔纳尔的话:“虚荣指标是无用的”,意思是不要监控那些让你看起来不错的指标,而是监控那些让你痛苦地发现什么是错的以及你需要纠正什么的不好的事情。如果测试没有伤害你,如果它们没有让你看到错误的地方,它们就没有价值。
测试你的数据,直到它受伤,这样你就可以修复问题并不断改进!
让我们把这个话题转到好的方面——我们可以做些什么来开始测试?我们怎样才能感受到好的痛苦?😅
最重要的是开始。从简单的事情开始。在我看来,测试我们的数据时,最大的价值可以从两件事情中获得:
- 如果数据质量不好,中断自动化管道
- 观察数据质量和意外数据/失败的管道
第一点是显而易见的,我们不想使用或发布坏数据,所以如果数据是坏的,我们就失败了。第二个几乎同样有价值:当你开始测试数据质量时,你很可能甚至不知道好数据是什么样子,也就是说,好数据的界限是什么,或者好的数据分布是什么样子?周末和工作日的数据可能会有变化,或者是您在处理初始数据集时没有注意到的季节性趋势。因此,为了保持数据质量,你需要学习和迭代好的数据实际上是什么样子的。
这对我们有用
到目前为止,我一直对我们实际上如何测试含糊其辞——所以让我们来看看我们的团队如何持续测试数据的一些更具体的例子。
正如我在一篇关于数据工程实践的文章中所写的:保持简单!如果你和我一样,经常使用 python 和 pandas,那么 great-expectations 是你开始数据测试之旅的绝佳起点。
远大期望的工作原理是构建期望套件(基本上是测试套件),随后用于验证数据批次。这些套件被保存为.json
文件,与您的代码一起存在。为了建立期望,您可以使用软件包中的一些基本分析器(或者编写自己的分析器——这相当简单),从一些建议开始,或者从头开始。让我们看一个简单的例子:
上面的例子假设您已经安装并初始化了这个包(我创建了这个 repo 来让您快速入门)。该示例展示了如何快速地将数据源添加到 great_expectations,将期望添加到期望套件,然后根据期望套件测试数据。您可以选择快速检查数据文档呈现。
关键特征
如前所述,我们想要瞄准的第一件事是,如果数据是坏的,就打破管道,然后我们想要观察数据质量随时间的变化。使用数据验证特性可以让您轻松地完成前一个任务:只需将验证作为管道的一部分运行,如果需要的话可以中断。后一个也包括在内,因为验证结果会告诉您哪里出错了,即哪些值是您没有预料到的。所以你马上就能知道哪里出了问题,并采取行动。
其他关键特性包括期望套件与代码共存,这对于版本控制来说是完美的。此外,您可以轻松地向您的套件添加注释,并捕获数据质量问题(它们可以很好地呈现在自动化数据文档中)。
因为所有这些都存在于您的代码库中(当然,您使用 git ),所以可以直接在团队中协作。在笔记本中添加编辑期望(带有可定制的 jinja 模板)是我们经常使用的功能:只需运行great_expectations suite edit <suite_name>
!
测试您的数据和数据分布。 great-expectations 允许您轻松测试数据分布。简单的均值、中值、最小值、最大值、分位数或更高级的东西,如库尔巴克-莱布勒散度。您可以使用mostly
关键字来表示大多数期望,以容忍一定比例的异常值。简单的内在期望会让你走得很远!
当然,还有更多:您可以根据管道运行时测试数据新鲜度,并使用运行时生成的评估参数或构建您自己的预期。还有很多内容我没有介绍,比如自动化数据文档、数据分析报告,它们都带有自动的 HTML 呈现,使共享和发布变得容易。
最重要的是,你可以很容易地参与到对代码库的贡献中来——这些人非常乐于助人,非常感激!
我们如何使用它
我们测试每个管道运行的输入和输出数据,与管道代码无关。我们的管道作为 kubernetes 作业运行,因此我们创建了一个简单的包装器,它读取命令行参数,解析数据集名称,将它们与一个期望套件进行匹配,并验证数据。
数据验证(蓝色菱形)与实际的管道代码分离,控制管道的失败、警报和发布。
上图是一个简单的图表,显示了当我们的任何管道运行时会发生什么:输入得到验证,然后实际的作业才运行。作业运行后,输出数据得到验证,然后数据才会被发布。如果对输出数据的验证失败,我们发布到一个failed
目的地,这样我们就可以在需要时检查输出。
疼痛
我们一把它投入生产,痛苦就开始了。我们花了大量的时间来分析数据,在非生产环境中运行所有带有新数据验证检查的管道。我们很自信。尽管如此,对于我们的数据以及数据随时间的变化,我们还有很多事情不知道。
几个星期以来,由于数据验证的原因,我们有失败的管道。有些是我们预料到的,但在某些时候,我觉得让我的一个支持轮换的同事经历这些真的很可怕:不断失败的运行,多次更新预期套件,清洗和重复。在开始的时候,很难弄清楚什么时候改变阈值/期望值,什么时候我们在看一个实际的问题。
只有痛苦。
增益
第一次痛苦消退后,我们很快看到奇怪的数据问题。在一个例子中,这使我们实际上发现了一些数据转换管道中的错误,这些错误只是随着时间的推移才显现出来。我们搞定了->利润!
在另一个例子中,我们实际上阻止了发布坏的预测——结果是从企业数据存储中复制数据的一个上游作业有不完整的数据😱。之前,这个 bug 已经导致我们在一个已知的事件中给出了一些不好的预测,其中有一些真实的💰对业务的影响。因此,阻止我们这样做是一笔可观的利润!
结论
这是真的:一分耕耘一分收获!
我们都知道我们必须检查我们的数据质量。一旦你打开了那罐蠕虫,你将很快感受到处理你发现的所有数据质量问题的痛苦。但好消息是:这是值得的。简单的数据测试可以让你走得更远,并显著提高你的产品质量。
在团队中,我与数据科学家密切合作,他们现在喜欢数据测试。他们欣赏这个工具以及它为团队和我们的产品创造的价值。我们用它不断改进我们的数据集,我们在测试上合作,测试不会因为管道代码而出错。
因此,尽管这一旅程可能会有痛苦,但这是非常值得的。立即开始测试您的数据!💯
测试你的技能:26 个(更多)数据科学面试问题和答案
来源: Pixabay
你能全部回答吗?
下面再来 26 个数据科学面试问答(以下是前 26 个)。这些问题按照数学和统计学到算法到深度学习到 NLP 的一般流程进行组织,其中穿插了数据组织问题。我建议在继续验证你的答案之前,先看看问题,花点时间想想答案。
无论你是大学生还是有经验的专业人士,每个人都可以花一些时间来测试(或更新)他们的技能!
你能全部回答吗?
来源:吉菲
1 |您可能会在数据中遇到哪些形式的选择偏差?
抽样偏差是一种系统误差,由于人口的非随机抽样,导致人口中的一些成员比其他成员少,例如低收入家庭被排除在在线民意调查之外。
时间间隔偏倚是指试验可能在一个极值提前终止(通常出于伦理原因),但该极值很可能由方差最大的变量达到,即使所有变量都具有相似的均值。
数据偏差是指选择特定的数据子集来支持一个结论,或者根据任意的理由拒绝不良数据,而不是根据先前陈述的或普遍同意的标准。
最后,流失偏倚是选择偏倚的一种形式,由参与者的损失引起,不考虑没有完成的试验对象。
2 |定义:错误率、准确度、灵敏度/召回率、特异性、精确度和 F 值。
其中 T 为真,F 为假,P 为正,N 为负,分别表示混淆矩阵中满足以下条件的项目数:
来源。图像免费共享和商业使用。
- 错误率:(FP + FN) / (P + N)
- 准确度:(TP + TN) / (P + N)
- 敏感度/召回率:TP / P
- 特异性:总氮/氮
- 精度:TP / (TP + FP)
- F-Score:精确度和召回率的调和平均值。
3 |相关性和协方差有什么区别?
相关性被认为是衡量和估计两个变量之间数量关系的最佳技术,它衡量两个变量的相关程度。
协方差衡量两个随机变量在周期中变化的程度。换句话说,它解释了一对随机变量之间的系统关系,其中一个变量的变化与另一个变量的相应变化相反。
4 |为什么 A/B 测试有效?
A/B 测试是对两个变量 A 和 B 的随机实验的假设测试。它的目标是识别例如网页中的任何变化,其中 A 组的客户被问候为“下午好”,而 B 组的客户被问候为“欢迎”,以查看哪一个可以促进销售。A/B 测试是有效的,因为它最大限度地减少了有意识的偏见——A 组的人不知道他们在 A 组,甚至不知道有 B 组,反之亦然。这是获取真实变量数据的好方法。然而,除了互联网业务,A/B 测试很难在任何环境下进行。
来源。图像免费共享和商业使用。
5 |你如何用一个骰子产生一个介于 1 和 7 之间的随机数?
一种解决方法是滚动骰子两次。这意味着有 6 x 6 = 36 种可能的结果。排除一种组合(比如 6 和 6),就有 35 种可能的结果。这意味着,如果我们指定五种掷骰子的组合(顺序很重要!)到一个数,我们可以生成一个 1 到 7 之间的随机数。
例如,假设我们掷出一个(1,2)。因为我们已经(假设地)将掷骰组合(1,1)、(1,2)、(1,3)、(1,4)和(1,5)定义为数字 1,所以随机生成的数字将是 1。
6 |区分单变量、双变量和多变量分析。
单变量分析是只对一个变量进行的统计分析技术。这可能包括饼图、分布图和箱线图。
双变量分析试图理解两个变量之间的关系。这可以包括散点图或等高线图,以及时间序列预测。
多变量分析处理两个以上的变量,以了解这些变量对目标变量的影响。这可以包括为预测或 SHAP 值/排列重要性训练神经网络,以找到最重要的特征。它还可以包括具有第三特征(如颜色或大小)的散点图。
7 |什么是交叉验证?它试图解决什么问题?为什么有效?
交叉验证是一种评估模型如何推广到整个数据集的方法。在传统的训练-测试-分割方法中,随机选择一部分数据作为训练数据,另一部分数据作为测试数据,这可能意味着模型在某些随机选择的测试数据部分表现良好,而在其他随机选择的测试数据部分表现不佳。换句话说,性能并不完全代表模型的性能,因为它代表了测试数据的随机性。
来源。图像免费共享和商业使用。
交叉验证将数据分成 n 段。该模型在数据的 n- 1 段上被训练,并在数据的剩余段上被测试。然后,在不同的一组 n -1 段数据上刷新和训练模型。重复此操作,直到模型获得整个数据的预测值(对结果进行平均)。交叉验证很有帮助,因为它提供了模型在整个数据集上的性能的更完整视图。
8 | naive Bayes 中的 Naive 是什么意思?
朴素贝叶斯算法基于贝叶斯定理,贝叶斯定理描述了基于可能与通风口相关的条件的先验知识的事件概率。该算法被认为是“幼稚”的,因为它做出了各种可能正确也可能不正确的假设。这就是为什么正确使用时它会非常强大——它可以绕过其他模型必须找到的知识,因为它假设这些知识是真实的。
9 | SVM 有哪些不同的果仁?
SVM 有四种核仁:
- 线性核
- 多项式核
- 径向基核
- Sigmoid 内核
10 |决策树过拟合的解决方案是什么?
决策树通常有很高的倾向性,因为算法的本质涉及到在数据中寻找非常合适的模式,并创建一个特定的节点来解决这个问题。如果放任不管,决策树将创建如此多的节点,以至于它将在训练数据上完美地执行,但在测试数据上失败。一种解决决策树过度拟合的方法叫做修剪。
过度适应数据的决策树。来源。图像免费共享和商业使用。
修剪是一种减少决策树大小的方法,它通过删除树中几乎没有分类能力的部分来实现。这有助于一般化决策树,并迫使它只创建数据结构所必需的节点,而不仅仅是噪声。
解释并给出协同过滤、内容过滤和混合过滤的例子。
协同过滤是推荐系统的一种形式,它仅仅依靠用户评级来确定新用户下一步可能喜欢什么。所有产品属性要么通过用户交互学习,要么被丢弃。协同过滤的一个例子是矩阵分解。
内容过滤是另一种形式的推荐系统,它只依赖于产品和客户的内在属性,如产品价格、客户年龄等。,提出建议。实现内容过滤的一种方式是找到简档向量和项目向量之间的相似性,例如余弦相似性。
混合过滤取二者之长,将内容过滤推荐和协同过滤推荐结合起来,实现更好的推荐。然而,使用哪种过滤器取决于现实环境,混合过滤可能并不总是确定的答案。
12 |在合奏中,装袋和助推有什么区别?
Bagging 是一种集成方法,其中通过从主数据集中随机选择数据来准备几个数据集(在几个子数据集中会有重叠)。然后,在几个子数据集中的一个上训练几个模型,并且通过一些函数聚集它们的最终决策。
提升是一种迭代技术,它调整最后一个分类的观测值的权重。如果一个观察被正确地分类,它试图增加观察的权重,反之亦然。提升降低了偏差误差,并建立了强大的预测模型。
13 |合奏中的硬投票和软投票有什么区别?
硬投票是当每个模型的最终分类(例如,0 或 1)被聚集时,可能通过平均值或模式。
软投票是将每个模型的最终概率(例如,分类 1 的 85%把握)汇总,最有可能是通过平均值。
软投票在某些情况下可能是有利的,但可能导致过度拟合和缺乏普遍性。
来源:吉菲。
14 |您的机器有 5GB 内存,需要在 10 GB 数据集上训练您的模型。你如何解决这个问题?
对 SVM 来说,部分适合就可以了。数据集可以分成几个较小的数据集。因为 SVM 是一个低计算成本的算法,它可能是这种情况下最好的情况。
在数据不适合 SVM 的情况下,可以在压缩的 NumPy 阵列上训练具有足够小批量的神经网络。NumPy 有几个压缩大型数据集的工具,它们被集成到常见的神经网络包中,如 Keras/TensorFlow 和 PyTorch。
深度学习理论已经存在了很长一段时间,但直到最近才变得流行起来。为什么你认为深度学习在最近几年激增了这么多?
深度学习的发展正在加快步伐,因为直到最近它才成为必要。最近从物理体验到在线体验的转变意味着可以收集更多的数据。由于转向线上,深度学习有更多的机会来提高利润和增加客户保留率,这在实体杂货店中是不可能的。值得注意的是,Python 中两个最大的机器学习模型(TensorFlow & PyTorch)是由大型公司谷歌和脸书创建的。此外,GPU 的发展意味着可以更快地训练模型。
(尽管这个问题严格来说与理论无关,但能够回答这个问题意味着你也着眼于你的分析如何在企业层面得到应用。)
16 |你如何初始化神经网络中的权重?
最传统的初始化权重的方式是随机的,将它们初始化为接近 0。然后,一个正确选择的优化器可以把权重放在正确的方向上。如果误差空间太陡,优化器可能难以摆脱局部最小值。在这种情况下,初始化几个神经网络可能是一个好主意,每个神经网络位于误差空间的不同位置,从而增加找到全局最小值的机会。
17 |没有设定准确的学习率会有什么后果?
如果学习率太低,模型的训练将进展非常缓慢,因为权重的更新很少。然而,如果学习率设置得太高,这可能导致损失函数由于权重的剧烈更新而不稳定地跳跃。该模型也可能不能收敛到一个误差,或者在数据太混乱而不能训练网络的情况下甚至可能发散。
18 |解释一个时期、一批和一次迭代之间的区别。
- Epoch:表示对整个数据集的一次运行(所有内容都放入训练模型中)。
- 批处理:因为一次性将整个数据集传递到神经网络的计算开销很大,所以数据集被分成几批。
- 迭代:一个批次在每个时期运行的次数。如果我们有 50,000 个数据行,批量大小为 1,000,那么每个时期将运行 50 次迭代。
19 |什么是三个主要的卷积神经网络层?它们通常是如何组合在一起的?
在卷积神经网络中通常有四个不同的层:
- 卷积层:执行卷积操作的层,创建几个图片窗口,概括图像。
卷积层。来源: Giphy 。
- 激活层(通常是 ReLU):给网络带来非线性,将所有负像素转换为零。输出变成校正的特征图。
- 池层:降低特征图维度的下采样操作。
通常,卷积层由卷积层、激活层和池层的多次迭代组成。然后,可能会跟随一个或两个额外的密集或下降图层以进一步概化,并以完全连接的图层结束。
20 |什么是辍学层,它如何帮助神经网络?
脱落层通过防止训练数据中复杂的共适应来减少神经网络中的过拟合。脱落层作为一个掩膜,随机阻止与某些节点的连接。换句话说,在训练期间,脱落层中大约一半的神经元将被停用,迫使每个节点携带更多被停用的神经元遗漏的信息。在最大池层之后,有时会使用退出。
应用辍学之前/之后的神经网络。来源。图片免费使用,有信用。
21 |在简化和基本的尺度上,是什么使新开发的 BERT 模型优于传统的 NLP 模型?
传统的 NLP 模型,为了熟悉文本,被赋予预测句子中下一个单词的任务,例如:“It's raining cats and”中的“dog”。其他模型可以另外训练它们的模型来预测句子中的前一个单词,给定该单词之后的上下文。BERT 随机屏蔽句子中的一个单词,并迫使模型预测该单词及其前后的上下文,例如:“It's _____ cats and dogs”中的“raining”
这意味着伯特能够理解语言的更复杂的方面,而这些方面不能简单地通过以前的上下文来预测。BERT 还有许多其他的特性,比如各种层次的嵌入,但是从根本上来说,它的成功来自于它阅读文本的方式。
22 |什么是命名实体识别?
NER,也称为实体识别、实体分块或实体提取,是信息提取的一个子任务,它试图定位非结构化文本中提到的命名实体并将其分类为诸如名称、组织、位置、货币值、时间等类别。NER 试图分离拼写相同但意思不同的单词,并正确识别名称中可能有子实体的实体,如“美国银行”中的“美国”。
给你一大堆推文,你的任务是预测它们是积极的还是消极的情绪。解释如何预处理数据。
由于推文充满了可能有价值的信息的标签,第一步将是提取标签,并可能创建一个一次性编码的特征集,其中如果有标签,推文的值为“1 ”,如果没有标签,则为“0”。对@ characters 也可以这样做(无论 tweet 指向哪个帐户都可能很重要)。推文也是被压缩的文字(因为有字符限制),所以可能会有很多故意的拼写错误需要纠正。也许推文中拼写错误的数量也会有所帮助——也许愤怒的推文中有更多拼写错误的单词。
删除标点符号虽然在 NLP 预处理中是标准的,但在这种情况下可能会被跳过,因为感叹号、问号、句点等的使用。与其他数据结合使用时可能很有价值。可能有三列或更多列,其中每行的值是感叹号、问号等的数量。但是,在将数据输入模型时,应该去掉标点符号。
然后,数据将被词条化和标记化,不仅有原始文本要输入模型,还有关于标签、@s、拼写错误和标点符号的知识,所有这些都可能有助于提高准确性。
24 |你如何找到两段文字之间的相似之处?
第一步是将段落转换成数字形式,使用一些选择的矢量器,如单词袋或 TD-IDF。在这种情况下,单词包可能更好,因为语料库(文本集合)不是很大(2)。此外,它可能更符合文本,因为 TD-IDF 主要是针对型号的。然后,可以使用余弦相似度或欧几里德距离来计算两个向量之间的相似度。
25 |在 N 个文档的语料库中,一个随机选择的文档包含总共 T 个术语。术语“hello”在该文档中出现了 K 次。如果单词“hello”在全部文档中出现了大约三分之一,那么 TF(单词频率)和 IDF(逆文档频率)的乘积的正确值是多少?
术语频率的公式是 K/T,IDF 的公式是总文档数对包含该术语的文档数的对数,或者是 1/1/3 的对数,或者是 3 的对数。因此,' hello '的 TF-IDF 值是 K * log(3)/T。
有没有一套通用的停用词?你什么时候会提高停用词的“严格性”,什么时候会对停用词更宽容?(对停用词宽容意味着减少从文本中删除的停用词的数量)。
Python 中的 NLTK 库中存储了普遍接受的停用词,但是在某些上下文中,它们应该被加长或缩短。例如,如果给定一个 tweet 数据集,停用词应该更宽松,因为每个 tweet 一开始就没有太多内容。因此,更多的信息将被压缩到简短的字符中,这意味着丢弃我们认为是无用的词可能是不负责任的。然而,如果给定,比如说,一千篇短篇小说,我们可能希望对停用词更严格一些,这样不仅可以节省计算时间,而且可以更容易地区分每篇小说,因为每篇小说都可能多次使用停用词。
感谢阅读!
来源: Giphy
你答对了几个?这些问题针对统计、算法、深度学习、NLP 以及数据组织和理解,这应该是衡量您对数据科学概念熟悉程度的一个很好的标准。
如果你还没有,请点击这里查看另外 26 个数据科学面试问题和答案。
用数据科学的魅力测试基于 Python 的 API 调用
尤利娅·卢卡希娜
使用 Python asyncio 和 concurrent.futures 包测试 ThreadPoolExecutor 函数中的线程数量(max_workers 参数)的实验。
软件测试一瞥
随着计算机应用程序趋向于被授予更多的人类决策权,软件工程行业已经认识到测试是开发过程的一个基本部分。
软件测试的方法多种多样。应用程序作为一个整体,或者作为集成系统,甚至是一个单元一个单元地被测试。我们有测试工程师、测试经理和测试人员。有提供外包手工测试的平台,也有自动化测试:从字面上看,应用程序操作其他应用程序,甚至经常模仿一个活生生的用户。
为什么要测试 API?
案例研究是关于测试 API 调用性能的。Python asyncio 和 concurrent.futures 包用于运行多个 API 调用。他们将循环运行分成池,并以并行方式通过几个池,从而同时执行多个调用。这减少了总的执行时间。
最佳线程数的问题出现在以下方面。让我们想象一下,有一个移动应用程序访问一个提供 API 的开源数据库。我需要编写一个 API 调用来成功检索数据,并将其集成到应用程序中。这种集成应该包括接收数据的第一次处理。
正如我将在下一节中展示的,在最初处理数据的过程中,我遇到了一些问题。数据只能以小块的形式下载,因此,迫使我提出多个请求。这可能会降低应用程序的性能,破坏用户体验。
这个事实让我更加关注 API 调用本身,尽管最初,我的主要任务是在应用程序内部构建一个数据管道。
因此,我开始尝试 API 调用。
这个实验的目的是找出一个最佳的线程数量,或者并行调用的最大数量。这个数字有其限制,取决于不同的因素。
在我经历了一些痛苦的错误之后,我设法坚持了一个特别的系统化的方法,我将会公开这个方法。这篇文章展示了完成测试代码的步骤,附在文章的最后。
对数据的第一次了解
我用一个电影数据库中的数据来说明本文,该数据库包含关于电影的信息,比如标题、发行日期、类型、演员和工作人员等等。API 调用应该获取特定电影类型的所有电影的数据。
我使用了一个通用的 Python 请求包来发出一个简单的请求,并检查这个请求是否有意义。我获取了一个 JSON 并将其打印到控制台。第一眼看上去不错。但后来似乎有一些人为强加的限制。我只能读到 500 页,收集 10,000 部电影。
发现页面限制
我查了一下电影的实际数量,结果是 20 部。
每页/API 请求的结果
我发出了第二个请求,表明我希望通过添加“&page=2”来提取第 2 页。结果是相似的,只是我得到了另外 20 部电影。
似乎我必须快速翻阅一大堆页面才能找到所有的电影。我添加了一个遍历 500 页的 for 循环。我抓取了 asyncio 和 concurrent.futures 包,通过使用多线程(我设置为 100)来加速 for 循环的执行。
这一过程在几秒钟内完成,并成功收集了 10,000 部电影。
import asyncioimport nest_asyncioimport concurrent.futuresimport requestsnest_asyncio.apply()movies = []async def main(url, m_workers):with concurrent.futures.ThreadPoolExecutor(max_workers=m_workers) as executor:loop = asyncio.get_event_loop()futures = [loop.run_in_executor(executor,requests.get,url + '&page=' + str(i),)for i in range(1, 501)]for response in await asyncio.gather(*futures):try:dict = response.json()for item in dict['results']:movies.append(item)except:passloop = asyncio.get_event_loop()loop.run_until_complete(main(my_url, 100))
但我必须记住,这个请求是为移动应用程序考虑的。到目前为止,我构建了请求代码,并使用运行在云中的 Google Colaboratory 检查了数据。
API 性能因素
正如一位 StackOverflow 开发者指出的那样,API 调用的速度取决于以下因素:
- 网络速度和延迟
- 服务器可用性和负载
- 数据有效负载的大小
- 客户端设备资源
- 客户代码
- 在所有这些因素中,我只能控制网络、设备和客户端代码。
网络
为了模拟移动网络速度,我将移动热点连接到笔记本电脑上。根据来自 Computerbild.de 的信息,我所在位置的 LTE 网络显示速度如下:
计算机网络服务器
我必须在测试中发现服务器的容量。我没有控制这个参数。
数据量
每个 API 调用发送同一个请求并下载相同数量的数据。这个实验不测试数据量的任何变化。
每个请求返回一个字典。它的结果部分—一个列表—包含多达 20 个条目。每个条目都是一个有 14 个键和值的字典。
使用另一个 JSON 解析器是可能的,但是我不认为标准解析器是个问题。
最终用户设备
由于数据将通过移动网络发送和接收,因此在实验中考虑了这一因素。
我在本地的一台 Mac 电脑上用一台 Jupyter 笔记本进行了 API 调用测试,这台电脑有两个内核,运行速度为 1.7 GHz。
网络连接,我用的是 iPhone 6。正如 Everymac.com 的所说:
它拥有双核,运行频率约为 1.4 GHz。
采用 A13 芯片的最新 iOS 设备拥有六个 CPU 核心,两个 Lightings 和四个 Thunders。第一批运行频率为 2.66 GHz。(信息来自一篇连线文章)。
在八核(2.3GHz 四核+ 1.6GHz 四核)或四核(2.15GHz + 1.6GHz 双核)处理器上运行,具体取决于国家或运营商。
在这方面,我的测试是一种最坏的情况。
客户代码
考虑到目前的限制,比如数据的规定结构,我只能在线程数量上做些调整。这基本上是客户端代码的主要变化。
在线程数上循环
所以,让我们回到主测试。
我从云转移到本地运行时,并做了一些网络和硬件降级。
在这个新环境中,我提出了几乎相同的要求。两次运行 100 个线程都抛出了类似的错误:
HTTPSConnectionPool(host=’api.host.org’, port=443): Max retries exceeded with url: apiurlwithapikey (Caused by NewConnectionError(‘<urllib3.connection.VerifiedHTTPSConnection object at 0x10e44f2d0>: Failed to establish a new connection: [Errno 8] nodename nor servname provided, or not known’))
这意味着我用尽了网络或服务器,或者两者都用尽了。我把它减速到 50 个线程,让它运行两次,但还是出现了同样的错误。仅用 10 个线程,我就安全下载了所有 10,000 部电影。
由于我仍然认为 10 个线程对于确保移动应用程序中令人满意的用户体验来说太少了,所以我引入了一个 for 循环,该循环多次运行 main()函数,以等于 10 的步长将最大活动线程数从 10 改为 100。
我添加了一个简单的跟踪方法,保存异步循环的持续时间、返回的电影数量、检查的页面数量和线程数量。持续时间是通过两个时间戳之间的差来测量的:一个在循环开始之前,一个在循环完成之后。
我把列表转换成字典,然后字典变成了熊猫的数据框。
为了防止服务器耗尽,我使用了时间包中的睡眠功能来安排通话中断。
import asyncioimport nest_asyncioimport concurrent.futuresimport requestsfrom datetime import datetimeimport timeimport pandasasync def main(url, m_workers):with concurrent.futures.ThreadPoolExecutor(max_workers=m_workers) as executor:try:loop = asyncio.get_event_loop()except:passtry:futures = [loop.run_in_executor(executor,requests.get,url + '&page=' + str(i),)for i in range(1, 501)]except requests.exceptions.ConnectionError:print("Connection refused")passfor response in await asyncio.gather(*futures):try:dict = response.json()for item in dict['results']:movies.append(item)except:passnummoviesList = []pagescheckedList = []itlastedList = []maxworkersList = []for threads in range(10,101,10):try:movies = []executorList = []n = datetime.now()loop = asyncio.get_event_loop()try:loop.run_until_complete(main(genre_18_url, threads))except:passnummoviesList.append(len(movies))pagescheckedList.append(len(movies) / 20)nn = datetime.now()diff = nn - nitlastedList.append(diff)maxworkersList.append(threads)except:print("interrupted at ", threads)time.sleep(10)resultsdict = {'threads': maxworkersList, 'movies': nummoviesList, 'pages': pagescheckedList, 'duration': itlastedList}testresults = pandas.DataFrame(resultsdict)
我确实得到了大部分空的响应,但是有几个返回了正常的结果:
测试结果运行 1
这种趋势并不十分明显:当线程数量增加时,API 调用会变得更快,在 10 到 30 个线程之间,速度会大大加快。但是持续时间(显示为完成一个请求的秒数)变得不稳定,并且又增加了 100 个线程。
所有红点表示没有获取任何数据的运行。乍一看,持续时间/速度、线程数量和检索到的数据之间没有联系。我再次运行包装循环,结果明显不同:
测试结果运行 2
趋势几乎是一样的:越匆忙——我使用的线程越多——得不到数据的可能性就越大。
另一件重要的事情,也是我继续测试的原因。从移动应用发出的每个 API 请求至少需要 8 分钟,这是一场灾难。没有用户会等这么久!
在下一个循环中包装代码
我本可以再按几次“运行”来重复循环,但是我决定添加另一个循环——我通常喜欢循环——并且我以代码结束:
import asyncioimport nest_asyncioimport concurrent.futuresimport requestsfrom datetime import datetimeimport timeimport pandasasync def main(url, max_workers):with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:try:loop = asyncio.get_event_loop()except:passtry:futures = [loop.run_in_executor(executor,requests.get,url + '&page=' + str(i),)for i in range(1, 501)]except:passfor response in await asyncio.gather(*futures):try:dict = response.json()for item in dict['results']:movies.append(item)except:passgrandresult = pandas.DataFrame(columns = ['threads', 'movies', 'pages', 'duration', 'run'])for x in range (1,11):nummoviesList = []pagescheckedList = []itlastedList = []maxworkersList = []for max_workers in range(10,101,10):movies = []try:executorList = []n = datetime.now()loop = asyncio.get_event_loop()try:loop.run_until_complete(main(my_url, max_workers))except:passnummoviesList.append(len(movies))pagescheckedList.append(len(movies) / 20)nn = datetime.now()diff = nn - nitlastedList.append(diff)maxworkersList.append(max_workers)except:print("interrupted at ", max_workers)time.sleep(10)resultsdict = {'threads': maxworkersList, 'movies': nummoviesList, 'pages': pagescheckedList, 'duration': itlastedList}testresults = pandas.DataFrame(resultsdict)testresults['run'] = xgrandresult = grandresult.append(testresults)print('end run ' + str(x))time.sleep(60)
正如你所看到的,有一个新的列:“运行。”它显示了运行的顺序。
我复制了一份,以防万一。然后我查看了每个线程数的平均性能。
我只跑了 9 次,所以电影栏变得有点混乱。如果平均值低于 450,则每次并未获取所有电影。但是我们仍然可以看到成功的运行是最慢的。最快的只是不完整。
让我们把它全部放在一张图表上。
API 调用持续时间与线程数量:按运行分组
对于这个可视化,我删除了三个或四个异常值。圆点的颜色表示外部 for 循环中的不同运行。没有任何颜色聚类表示单次运行在性能上没有区别。只有一些线程起作用。
然后,我根据是否获取了任何数据来给这些点着色(深紫色或 0 表示没有数据,黄色或 1 表示下载了所有电影)。
API 调用持续时间与线程数量:按成功或失败分组(0 =失败,1-成功)
最后一个可视化结果表明,坚持 10 到 30 个线程的范围是有意义的。但即使是这些也大多不可靠。
测试较窄的范围
在陷入沮丧之前,我想做最后一次尝试,开始另一个实验,在 1 到 30 的范围内测试线程数,并制作一个 1 的小缺口。
下一张图表显示,几乎没有避免空白回答的选项。然而,一个接一个地发送请求持续的时间太长了:175 秒。
API 调用持续时间与线程数量:按成功或失败分组(0 =失败,1-成功)
我可以选择过滤掉最长的持续时间,但是这些都是可靠的配置。最后我把 25 秒以上的都去掉了。在图表的网格布局上,可以看到这是一个大部分点所在的范围,还有很多黄色的点。
API 调用的持续时间与线程数量:按成功或失败分组(0 =失败,1-成功)。持续时间范围:从 1 到 25。
没有一个群集具有可接受的持续时间和可靠的结果。在从 9 到 14 个线程的范围内,所有的点都是黄色的,因此,这些线程数确实下载了电影数据。
在这里,最终的选择变得棘手。
9 到 13 个线程缩放
10 线程和 13 线程点簇的持续时间最短。13 线程点簇比其他簇有更多的低持续时间。9 线程集群似乎是最稳定的:所有的点都挤在一起。
是时候做一些描述性统计了。我计算了平均值、中间值、偏差等。并按从低到高的顺序排列,最低的得到最高的等级。
不太科学,但最终有可能确定最佳结果。
根据基于中值、平均值和标准偏差值的排名的最佳线程数量
这是给定条件下的最佳线程数。
尽管如此,移动应用程序会让用户再等 13 秒来完成基于 API 调用的操作。这是采取下一步性能优化措施的好理由,比如减少要检索的数据量,或者考虑其他来源。这也是重新思考移动应用背后的商业决策的一个很好的理由。
为什么都用 Python?
至少有三个原因可能会导致您更喜欢使用 Python 作为编写软件测试的语言:
- 你一般爱 Python!
- 你想要一个持续的检查,在你的服务器上永久运行。
- 您希望将结果传输到一个 AI 中,该 AI 会自动纠正被测试的主软件中的线程数量(或任何其他参数)。
一些要带走的东西
当然,我用一段清晰的代码说明了这篇文章,这段代码实际上是从一片混乱中诞生的。因此,关于如何不搞砸你的测试的一些学习。
- 了解你的数据。我进行了第一个实验,循环浏览 1000 页。然后我发现只有 500 个可用的键,当我最终想要访问返回的字典中的所有键时。
- 追踪你的测试。运行一次尝试后,不要在 Jupyter 或 Colab 的同一个单元格中编辑代码:而是复制粘贴!以前的版本反而可以变成更好的。
- 花时间构建有意义的数据可视化。在我构建了包含成功和失败运行的图表后,我可以缩小范围并消除第 10 个缺口。
- 检查隐藏的依赖性和系统模式:使用你的数据 vizzes。
- 一步一步地发展你的最终代码。更容易还原自己的逻辑来报告测试结果。
- 小步前进。匆忙最终会耗费更多的时间。
- 从一个描述性的和更一般的方法开始,定位你的挑战,然后缩小你的焦点。我从一个单一的 API 请求开始,并把它发展成一个由三个循环相互包裹的代码。
- 要系统!
特别是关于 API 测试,我也想分享一些技巧:
- 在代码中放入大量的“try-except”对,后跟“pass”语句。这有助于您在不中断循环的情况下进入下一次运行。运行可能会由于与测试无关的临时网络问题而终止。
- 放一个具体的例外,比如:
except requests.exceptions.ConnectionError:print(“Connection refused”)pass
它会给你一个提示,为什么会出现这个问题。您还可以获取本机错误消息,我在“循环遍历数字…”一节中展示了这一点
- 在多线程请求之间放置一个“睡眠”中断。不要让服务器生气!
- 当尝试不同数量的线程时:从一个间隙开始,跟踪一个成功的范围;消除间隙并测试增益。
- 尝试不同的网络容量和硬件。
- 模仿最终用户设置。
在这里,您可以找到本文的所有代码片段,包括最终的代码和数据可视化片段:
测试 API 调用中的线程数:asyncio 和 concurrent.futures 包(Python)colab.research.google.com](https://colab.research.google.com/drive/1TX98-OpRYkdGEA5Onu81EDfv5GOOKHH2)
测试愉快!
使用 pytest 在 Google Cloud Composer 上测试气流作业
无需重新发明轮子的可靠 CI/CD
马克·克林在 Unsplash 上的照片
Airflow 是一个开源的工作流管理平台,由 Airbnb 开发,现在由 Apache 维护。它基本上是类固醇上的 cron[1],具有无限扩展的理论能力,将这一特性的复杂性完全抽象给最终用户。你只需要写代码,决定它什么时候运行,剩下的就交给 Airflow 了。部署气流并不容易,需要深厚的运营知识和工具,如 Kubernetes 和芹菜。出于这个原因,许多云提供商提供托管气流部署,比如天文学家. io 或谷歌云合成器,这里仅举最著名的例子。设置一个运行 Airflow 的集群只需点击几下,从那时起,几乎所有的东西都可以使用 Airflow Web UI 进行配置。
在 HousingAnywhere 我们几乎在所有事情上都使用谷歌云平台,决定在 Cloud Composer 上部署 Airflow 是一个自然的结果。生成的环境开箱即用,带有 CeleryExecutor,dags 文件夹存储在 Google 云存储中。你需要在气流上运行的任何东西都必须首先被推到那个桶中,并且作业产生的任何东西都将被存储在同一个桶上的特定文件夹中。
Airflow 完全用 Python 编写,所有工作流程都是通过 Python 脚本创建的。实际上,这不是一个限制,因为任务本身(组成 DAG 的原子工作单元,一组相互依赖的任务)可以用任何语言编写,并通过 bash 或 Docker 运行。在 HousingAnywhere,我们将 Airflow 用于大量不同的用例,从移动数据到网络抓取,当然,我们在 Github 上发布所有内容。在试验的最初几天,我们手动将主分支上刚刚合并的所有新工作推到桶中:这是可行的,但在规模上是不可持续的,存在拥有不同真实来源的巨大风险,一个在 Github 上,一个在 GCS 上。
使用 Google Cloud Build,我们设计了一个自动化流程,由我们的气流存储库的主分支上的任何相关更改触发。无论 master 上发生什么,都会自动与 GCS bucket 同步,不再需要任何人手动处理这个过程。基本上是一张关于气流的 CD。
Google Cloud Build 上触发的作业的历史记录
CloudBuild 语法和与 Google Cloud Composer API 的集成允许我们做更多的事情,序列化 Github 上的绝大多数气流环境:不仅仅是 dags 文件夹,还有任务所依赖的其他重要参数,如变量。这里的主要思想是,如果发生了不好的事情,我们可以几乎完全重建我们的环境,只需在新的 Airflow 集群上运行相同的 CloudBuild 作业,而无需任何努力或返工。
谷歌云构建步骤克隆气流变量
然而,这样的自动化流程需要仔细检查主分支上合并的内容,防止开发人员触发可能破坏环境的自动化任务。在制作 CD 时,我们开始设计一个自动化的 CI,能够可靠地测试我们的分支,如果有问题,可能会亮起红灯。
诚然,有意义的气流部署是复杂的,但建立一个能够顺序运行任务的快速环境并不复杂。创建一个本地气流部署实际上是三个命令的事情,底层的 Python 代码库使它非常轻量和灵活。
Bash 脚本需要启动一个简单的气流环境
使用 pytest (在 HousingAnywhere,我们已经使用这个库在我们所有的配置项上测试 Python)很容易模拟出运行一个任务所需的所有对象,幸运的是,这项工作可以在我们希望测试的所有气流实例之间共享。每个开发人员只需负责导入那些资源,并负责自己的测试。使用 Airflow 的复杂性几乎完全消除了,针对代码特性编写有意义的测试实际上是几十年前的最佳实践。
感谢巴斯·哈伦斯拉克的灵感
我们已经为所有的操作符编写了测试,并对 dags 文件夹中的 DAGs 进行了一致性检查,以便不在那里合并一些中断的(或循环的)东西。
DAGs 文件夹上一致性检查的代码
总结一下,现在只需要为您最喜欢的 CI 工具定义测试管道(在 HousingAnywhere,我们使用 Jenkins,但是无论什么都可以),然后运行 pytest 命令。如果分支是绿色的,则合并是安全的。包含测试的文件夹不需要被推到 GCS 上的桶中,并且可以被保留在设计的 CD 之外。
CI/CD 的简化架构
一个例子,使用雪花测试 DAGs】
在这里展示一个例子,展示我们如何处理在我们的云数据仓库雪花上运行的测试气流任务,可能会很有趣。如果使用 pytest 很容易模拟出 Postgres 实例,那么雪花的唯一解决方案就是在云上创建一个测试环境,尽可能与生产环境相似,并对其运行所有测试,注意用有意义的数据和资源填充它。
为此,通过雪花查询引擎提供的 GET_DDL 函数非常有用,它输出重新创建作为参数传递的表所需的 SQL DDL 查询。在测试时创建测试环境实际上就是执行几个 sql 查询。从监控的角度来看,将测试数据库连接到一个单独的仓库是明智的,这样可以控制成本并避免与预测资源的冲突。
雪花是直接从詹金斯启动的吊舱进入的
必须将生产数据的子集转储并复制到测试环境中,可能会在测试会话结束时擦除它们。但是这主要是基于特定的需求和要测试的任务的功能。Python 的雪花连接器在这个方向上提供了很大的自由度,但是对于一些非常特殊的用例,我们也将 SQLAlchemy 与 Pandas 结合使用。
在 Airflow 方面,所有到数据库或外部资源的连接都应该通过钩子来处理。SnowflakeHook 可以从默认的 Airflow 安装中获得,使用它的工作量非常有限,只需通过 pip 导入正确的资源即可。
可以使用 pytest mocker 库向任务公开测试凭证。每次函数调用 get_connection 时,解释器都会返回这些凭证,而实际上不会向 Airflow 请求任何东西。
测试的实际内容是特定于任务的,在这个例子中,我们希望为在雪花表格中推送新广告的工作编写测试。我们想强调我们没有使用任何不包含在任务结果中的广告客户(可能由另一个广告客户生成,之前执行过)。
测试操作员时执行的一些检查
前置集和后置集是直接针对雪花表运行查询来填充的。
[1]这个惊人的定义不是我的。马克·纳格尔伯格的功劳
在 R 中测试有序数据和回归的另一种可视化
Dataviz
使用 brms 和 ggplot2 仅在一个图上显示您的交互
有序数据无处不在(好吧,也许不是无处不在,但仍然很常见)。例如,在心理学(我的科学领域)中,人们经常使用有序数据,例如在使用李克特量表时。然而,可视化有序数据和用于分析有序数据的回归并不容易。当然,你可以用经典的 t 检验分析你的数据,并绘制柱状图,但这并不推荐( Liddell & Kruschke,2018 )。谢天谢地,你可以通过神奇的 brms R 软件包学习如何更好地打点( Bürkner & Vuorre,2018 )。这篇文章不是关于如何分析有序数据,而是关于如何可视化它。如果你想学习分析,你可以阅读上面引用的文章或者看本章出自所罗门·库尔茨。
“困难”(?)
有几个选项可以显示有序回归的结果。brms R 包( Bürkner,2018 )提供了一种很好的方式,用 conditional_effects 命令从序数模型中提取条件效果。在之前的研究中,我们已经将此方法与 Amélie Beffara Bret 一起使用,您可以在下面找到一个(定制)输出示例:
使用 brms 的有序回归可视化示例
我们可以看到,我们有两个预测值,称为“RWA”(连续,在 x 轴上)和“Conditioning”(两个值显示在不同的图中)。在 y 轴上,我们有顺序结果(“评估”),图例显示了概率等级。当然,这不是绘制这种数据和回归的唯一方法。结果可能是图例和概率在 y 轴(例如,见马修·凯的这篇博文)。然而,在这两种情况下,重要的一点是每个响应的概率在图上占一个维度,或者在图例中,或者在 y 轴上。因此,这个维度不能用于预测。这非常重要,因为这意味着当你对交互感兴趣时,你需要几个图(或 3D 图)。这不一定是一个大问题,但我想测试一个新的(至少对我来说)可视化选项。
种“解”(?)
下面是我最近测试的一个例子:
那么我们这里有什么?我们有两个预测值,即“我必须吃的水果”和“我必须吃带… 的水果”。这是两个分类预测值,一个在 x 轴上,另一个在图例上。在 y 轴上,我们可以找到具有 4 个可能值的有序结果。抖动的点是所有参与者的真实反应。我发现绘制原始数据非常重要。但是如果我们只画这些点,我们就没有模型的估计。正如我上面提到的,为有序模型绘制条件效应通常需要为每个响应的概率使用一个维度。在这里,我选择在抖动点上方的另一个小轴上绘制这个概率(以及它的可信区间 CI)。这允许我们在一个图上显示相互作用,因为我们有另一个第二预测值的自由维度。虚线简单地连接了每种情况下的一种“总体”响应估计(根据 brms 模型计算)。尽管这种方法远非完美,但它允许我们(imho)绕过一些通常在顺序模型可视化中遇到的限制。那么,我们该怎么做呢?如果你已经熟悉 brms 和 ggplot2,这并不复杂。让我们来看看。
乱码
在这里你可以下载重现例子所需的一切。
首先,我们需要一个数据框。我使用了一个从我正在做的项目修改而来的数据框架(这让我想到了我在这里展示的 dataviz)。我们有两个独立变量:水果(黑莓对树莓)和我们如何吃它(勺子对叉子对刀子)。我们的因变量是 4 个有序水平的被试的反应:“我讨厌它”、“我不喜欢它”、“我喜欢它”、“我喜欢它”,分别编码为 1、2、3、4。在这个实验中,我们有几个参与者和几个实验者。都有一个随机 ID。
library(tidyverse)
library(brms)
library(extrafont)
library(hrbrthemes)##### Import df #####
fruit_df <- read_csv("fruit_df.csv")
然后我们运行我们的(当然是顺序的)模型。我们想要估计水果的恒定效果,我们如何吃水果,以及这两个变量之间对反应的相互作用(即参与者如何喜欢它)。我们也包括由参与者和由实验者改变截距。
##### Run model #####
fit_fruit <- brm (
formula = resp ~ fruit * cutlery + (1 | ppt) + (1 | exp),
data = fruit_df,
warmup = 1000,
iter = 2000,
chains = 4,
cores = parallel::detectCores(),
control = list(adapt_delta = 0.8, max_treedepth = 10),
family = cumulative (link = "probit", threshold = "flexible")
)summary(fit_fruit)
我们不会在这里谈论模型的质量(或者如何检查它),这超出了范围。我们就当模型还可以吧。现在我们需要从模型中获得估计值,因为我们希望能够绘制它。更准确地说,我们想知道两个预测值的每个组合中每个响应的概率(以及与每个估计值相关的可信区间)。
##### Get estimates from the model ###### set conditions for the "fruit" predictor
cond_Blackberries <- data.frame(fruit = "Blackberries",
cond__ = "Blackberries")cond_Raspberries <- data.frame(fruit = "Raspberries",
cond__ = "Raspberries")# get estimates of the probability of each response in each combination of the two predictorsconditional_Blackberries <- conditional_effects(
effects = "cutlery",
conditions = cond_Blackberries,
fit_fruit,
re_formula = NULL,
robust = TRUE,
categorical = TRUE,
probs = c(0.025, 0.975),
plot = FALSE
)[[1]]conditional_Raspberries <- conditional_effects(
effects = "cutlery",
conditions = cond_Raspberries,
fit_fruit,
re_formula = NULL,
robust = TRUE,
categorical = TRUE,
probs = c(0.025, 0.975),
plot = FALSE
)[[1]]conditional_merged <-
rbind(conditional_Blackberries, conditional_Raspberries)
为了画出虚线,我们还需要对两个预测因子的每个组合的反应进行“总体估计”。这一部分不是最相关的,但它可以(可能)帮助想象兴趣的影响。“总体估计”给出了根据每个响应水平的中值概率计算的每个条件下的中值响应的概念。
# Compute overall response estimates (for the dashed lines on the plot)overall_list <- conditional_merged %>%
mutate(overall_resp = as.numeric(effect2__) * estimate__)overall_sum <-
aggregate(data = overall_list, overall_resp ~ fruit * cutlery, sum)
现在我们可以开始策划了。首先,我们通过显示每个响应来绘制原始数据(一个响应=一个点)。
#### Starting plot ##### Get font
hrbrthemes::import_roboto_condensed()
loadfonts()# Order levels
level_order <- c('Spoon', 'Fork', 'Knife')#### Plot raw data ####
plot_fruit <-
ggplot(fruit_df, aes(
x = factor(cutlery, level = level_order),
y = resp,
fill = fruit
)) +
geom_point(
data = fruit_df,
aes(
x = factor(cutlery, level = level_order),
y = resp,
colour = fruit,
fill = fruit
),
position = position_jitterdodge(
jitter.width = .08,
jitter.height = 0.16,
dodge.width = .2
),
size = 2,
alpha = .2,
shape = 20,
inherit.aes = FALSE
)
然后,我们绘制“第二轴”。图中的灰色小竖线。我们将在这些“轴”上绘制我们的概率估计及其 CI。这是代码中最糟糕的部分,因为我没有比手动设置坐标更好的解决方案。 geom_segment 否则表现不佳。
#### Prepare secondary axes ####
plot_fruit <- plot_fruit + geom_segment(
data = conditional_merged,
aes(
x = cutlery,
xend = c(
1.95,
2.95,
0.95,
1.95,
2.95,
0.95,
1.95,
2.95,
0.95,
1.95,
2.95,
0.95,
2.05,
3.05,
1.05,
2.05,
3.05,
1.05,
2.05,
3.05,
1.05,
2.05,
3.05,
1.05
),
y = as.numeric(effect2__) + 0.2,
yend = as.numeric(effect2__) + 0.5,
linetype = "1"
),
color = "gray20",
size = 0.3,
position = position_dodge(width = .2),
lineend = "round",
show.legend = FALSE
)
现在我们应用同样的方法,但是这次是针对概率估计和它们的 CI。
#### Plot estimates and CI ####
plot_fruit <- plot_fruit + geom_segment(
data = conditional_merged,
aes(
x = cutlery,
xend = c(
1.95,
2.95,
0.95,
1.95,
2.95,
0.95,
1.95,
2.95,
0.95,
1.95,
2.95,
0.95,
2.05,
3.05,
1.05,
2.05,
3.05,
1.05,
2.05,
3.05,
1.05,
2.05,
3.05,
1.05
),
colour = fruit,
y = as.numeric(effect2__) + 0.2 + lower__ * 0.3,
yend = as.numeric(effect2__) + 0.2 + upper__ * 0.3,
linetype = "1"
),
size = 2,
position = position_dodge(width = .2),
lineend = "round",
show.legend = FALSE
) + geom_point(
data = conditional_merged,
aes(
x = cutlery,
y = as.numeric(effect2__) + 0.2 + estimate__ * 0.3,
colour = fruit,
),
size = 4,
position = position_dodge(width = .2),
show.legend = FALSE
)
我们画虚线。它们将我所说的“总体估计”联系起来。
#### Dashed lines ####
plot_fruit <- plot_fruit + geom_line(
data = overall_sum,
aes(
x = factor(cutlery, level = level_order),
y = overall_resp,
group = fruit,
colour = fruit
),
linetype = 3,
size = 0.5
)
我们添加文本来说明每个响应级别的含义。
#### Labels ####
plot_fruit <- plot_fruit + annotate(
"text",
x = 0.4,
y = 1.07,
hjust = 0,
color = "gray40",
label = "I hate it",
size = 7
) +
annotate(
"text",
x = 0.4,
y = 2.07,
hjust = 0,
color = "gray40",
label = "I don't like it",
size = 7
) +
annotate(
"text",
x = 0.4,
y = 3.07,
hjust = 0,
color = "gray40",
label = "I like it",
size = 7
) +
annotate(
"text",
x = 0.4,
y = 4.07,
hjust = 0,
color = "gray40",
label = "I love it",
size = 7
)
最终定制,我们可以绘图!
#### Custom ####
plot_fruit <- plot_fruit +
scale_colour_brewer(palette = "Dark2",
name = "The fruits \nI have to eat") +
scale_fill_brewer(palette = "Dark2",
name = "The fruits \nI have to eat") +
theme_minimal() +
scale_x_discrete(name = "I have to eat the fruits with a") +
scale_y_continuous(name = "Responses",) +
theme_ipsum_rc(
base_size = 25,
subtitle_size = 25,
axis_title_size = 30
) +
guides(alpha = FALSE,
colour = guide_legend(override.aes = list(shape = 15, size = 7))) +
scale_alpha_continuous(range = c(0.2, 1)) +
theme(text = element_text(size = 30))plot_fruit
不滚动了,这里又是剧情。
就是这样。没什么特别的,但这是(可能是?)一个选项。当然,如果在论文中使用,这种情节需要有充分的解释。但那是后话了。如果您对可能的改进有任何意见或建议,请告诉我。感谢阅读!
感谢
非常感谢拉迪斯拉斯·纳尔博奇克、伊索尔特·赫贾-布里沙德、贝特朗·贝法拉和艾米里·贝法拉·布雷特对本文早期版本的评论和帮助。
脚注
你可以在这里找到(凌乱++的)代码,在这里找到预印本*,在这里找到论文(法文)里面的情节。***
你可以在这里 找到(凌乱++的)代码 ,在这里 找到预印本 。
在冠状病毒文章上测试基于 BERT 的问答
COVID19 图像
简介
目前世界大部分地区都受到了 T2 新冠肺炎疫情 T3 的影响。对我们许多人来说,这意味着在家隔离,社会距离,工作环境的破坏。我非常热衷于使用数据科学和机器学习来解决问题。如果你是一家医疗服务公司,正在寻找数据科学帮助来应对这场危机,请通过这里的联系我。
世界各地的媒体不断报道疫情——最新的统计数据、政府的指导方针、保护自己安全的提示等等。整理所有的信息会很快让人不知所措。
在这篇博客中,我想分享你如何使用基于 BERT 的问答来从关于病毒的新闻文章中提取信息。我提供了关于如何自己设置它的代码提示,并分享了这种方法在哪里有效以及何时会失败。我的代码也上传到了 Github 。请注意,我不是健康专家,本文的观点不应被解释为专业建议。
什么是问答?
问题回答是自然语言处理中计算机科学的一个领域,它包括建立能够从给定的文本中回答问题的系统。在过去的 2-3 年里,这是一个非常活跃的研究领域。最初的问题回答系统主要是基于规则的,并且只限于特定的领域,但是现在,随着更好的计算资源和深度学习架构的可用性,我们正在获得具有通用问题回答能力的模型。
现在大多数表现最好的问答模型都是基于变形金刚架构的。为了更多地了解变形金刚,我推荐了这个 youtube 视频。转换器是基于编码器-解码器的架构,其将问答问题视为文本生成问题;它将上下文和问题作为触发器,并试图从段落中生成答案。BERT、ALBERT、XLNET、Roberta 都是常用的问答模型。
小队— v1 和 v2 数据集
斯坦福问答数据集(SQuAD )是用于训练和评估问答任务的数据集。小队现在发布了两个版本——v1和 v2 。这两个数据集之间的主要区别在于,SQuAD v2 还考虑了问题在给定段落中没有答案的样本。当一个模型在小队 v1 上训练时,即使没有答案,模型也会返回一个答案。在某种程度上,你可以使用答案的概率来过滤掉不太可能的答案,但这并不总是有效。另一方面,在 SQuAD v2 数据集上进行训练是一项具有挑战性的任务,需要仔细监控精度和超参数调整。
构建和测试班伯特模型
随着这种病毒在世界范围内的爆发,有大量的新闻文章提供了关于这种病毒的事实、事件和其他消息。我们在模型从未见过的文章上测试问答如何工作。
为了展示这种能力,我们从 CNN 选择了一些信息丰富的文章:
来自 huggingface 的 transformers 是一个令人惊叹的 github repo,其中他们编译了多个基于 transformer 的训练和推理管道。对于这个问答任务,我们将从 https://huggingface.co/models下载一个预先训练好的小队模型。本文中我们将使用的模型是Bert-large-uncased-whole-word-masking-fine tuned-squad。从模型名称可以明显看出,该模型是在大型 bert 模型上训练的,该模型具有不区分大小写的词汇并屏蔽了整个单词。使用该模型我们需要三个主要文件:
- Bert-large-un cased-whole-word-masking-fine tuned-squad-config . JSON:这个文件是一个配置文件,其中包含代码将用来进行推理的参数。
- Bert-large-un cased-whole-word-masking-fine tuned-squad-py torch _ model . bin:这个文件是实际的模型文件,它包含了模型的所有权重。
- Bert-large-un cased-whole-word-masking-fine tuned-squad-TF _ model . H5:该文件具有用于训练该文件的词汇模型。
我们可以使用以下命令加载我们已经下载的模型:
tokenizer = AutoTokenizer.from_pretrained(‘bert-large-uncased-whole-word-masking-finetuned-squad’, do_lower_case=True)model = AutoModelForQuestionAnswering.from_pretrained(“bert-large-uncased-whole-word-masking-finetuned-squad”)
总的来说,我们将代码组织在两个文件中。请在我的 Github 上找到以下内容:
- 问题 _ 回答 _ 推理. py
- 问题 _ 回答 _ 主页. py
question _ answering _ inference . py 文件是一个包装文件,它具有处理输入和输出的支持函数。在这个文件中,我们从文件路径中加载文本,清理文本(将所有单词转换为小写后删除停用单词),将文本转换为段落,然后将其传递给 question_answering_main.py 中的 answer_prediction 函数。answer_prediction 函数返回答案及其概率。然后,我们根据概率阈值过滤答案,然后显示答案及其概率。
question_answering_main.py 文件是一个主文件,它具有使用预训练模型和标记器来预测给定段落和问题的答案所需的所有功能。
此文件中的主要驱动程序函数是 answer_prediction 函数,它加载模型和记号化器文件,调用函数将段落转换为文本,将文本分段,将特征转换为相关对象,将对象转换为批次,然后用概率预测答案。
为了运行脚本,请运行以下命令:
python question_answering_inference.py — ques ‘How many confirmed cases are in Mexico?’ — source ‘sample2.txt’
该程序采用以下参数:
- ques:问题参数,要求用单引号(或双引号)将问题括起来。
- source:该参数具有包含文本/文章的文本文件的源路径
模型输出
我们在冠状病毒的几篇文章上测试了这个模型。:
冠状病毒的爆发提醒我们,我们触摸自己的脸次数太多了。减少这种意愿…
edition.cnn.com](https://edition.cnn.com/2020/03/08/health/coronavirus-touching-your-face-trnd/index.html) [## 美国冠状病毒病例超过 20 万。更多的州说呆在家里
越来越多的数据显示,没有症状的人正在加剧冠状病毒的传播,这让高层官员重新思考是否…
edition.cnn.com](https://edition.cnn.com/2020/04/01/health/us-coronavirus-updates-wednesday/index.html)
在下面的例子中,我们有传递给模型的文本和问题,以及返回的答案及其概率。
问答示例 1
以上回答正确。该模型能够以很高的置信度选择正确的答案。
问答示例 2
这是一个更棘手的问题,因为文章中有几个共享的统计数据。冰岛的一项研究显示,大约 50%的携带者没有症状,而疾病控制中心估计有 25%。看到这个模型能够选择并返回正确的答案真是令人惊讶。
同一篇文章后来谈到了干预的效果以及它可以避免多少死亡。相关文本复制如下。然而,当运行代码时,我们传递的是整篇文章,而不是下面的子集。由于 SQUAD 模型对可以处理多少文本有限制,所以我们在多个循环中传递完整的文本。如下所示,这意味着模型会返回几个答案。这里概率最高的第一个答案是正确的。
问答示例 3 —返回多个答案
下面分享了另一个发生这种情况的例子。正确答案是迈克·瑞安是世卫组织突发卫生事件项目的负责人。该模型能够以 96%的概率发现这一点。然而,它返回福奇博士作为一个低概率的答案。在这里,我们也可以使用概率分数来得到正确的答案。
问答示例 4 —多个答案
上面的例子显示了从很长的新闻文本中获取答案的能力有多强。另一个惊人的发现是在小队训练的模型有多强大。他们能够很好地概括以前从未见过的文本。
所有人都说,这种方法在所有情况下都完美地工作仍然有一些挑战。
挑战
使用第一小队回答问题的几个挑战是:
1)很多时候,即使答案不存在于文本中,模型也会提供足够高概率的答案,导致误报。
2)虽然在许多情况下,答案可以通过概率正确排序,但这并不总是防弹的。我们观察过错误答案与正确答案的概率相似或更高的情况。
3)该模型似乎对输入文本中的标点符号敏感。在向模型输入内容时,文本必须是干净的(没有垃圾单词、符号等),否则可能会导致错误的结果。
使用 v2 小队模型代替 v1 小队可以在一定程度上解决前两个挑战。SQuAD v2 数据集在训练集中有样本,其中没有所提问题的答案。这允许模型更好地判断何时不应该返回答案。我们有使用 v2 小队模型的经验。如果您有兴趣将此应用到您的用例中,请通过我的网站联系我。
结论
在本文中,我们看到了如何轻松实现问题回答来构建能够从长篇新闻文章中查找信息的系统。我鼓励你自己尝试一下,在隔离期间学习一些新的东西。
总的来说,我对 NLP、变形金刚和深度学习非常感兴趣。我有自己的深度学习咨询公司,喜欢研究有趣的问题。我已经帮助许多初创公司部署了基于人工智能的创新解决方案。请到 http://deeplearninganalytics.org/.的来看看我们吧
参考
- 变压器模型纸:【https://arxiv.org/abs/1706.03762
- BERT 模型解释:https://towards data science . com/BERT-Explained-state-of-art-language-Model-for-NLP-F8 b 21 a9b 6270
- 抱紧脸
测试是不确定性的疫苗
为什么测试和实验是成功商业成果的关键
商业实验是避免代价高昂的错误和做出有利可图的决策的关键。从数据库时钟修改的图像。
生命中只有三件事是确定的——死亡、税收和不确定性。
现实生活是不确定的。这是概率性的。如果你是执行决策者,你可能已经知道了。举个例子,让我们来比较一下重塑了美国和世界零售业的两个最具代表性的零售商:亚马逊和 JCPenney。当亚马逊每个季度都达到不可思议的高度时,JCPenney 却在破产的边缘苦苦挣扎。怎么会变成这样?早在 1994 年,JCPenney 不就是走在时代前列,成为第一批上线的零售商之一吗?然而,一旦灾难降临,他们就再也没有恢复过来。
这场“灾难”始于 2010 年至 2013 年间的一系列失误,当时 JCPenney 发起了一场大规模的品牌重塑活动,同时改变了营销和折扣策略。为赢得更多消费者而付出的努力最终却疏远了他们。你可以把责任归咎于很多决策,但在 10,000 英尺高空的情况更清楚:JCPenney 实施了巨大的变革,而且是一次实施很多变革,没有在消费者身上进行测试。
在没有任何测试或实验的情况下实施变革,就像拿你的企业玩俄罗斯轮盘赌。
像 JCPenney 这样的伟大王朝不是因为决策失误而灭亡的:他们是因为直到为时已晚才知道自己错了。在花大价钱彻底改变你的商业策略之前,为什么不验证一下你提议的改变在小范围内是否有效呢?给自己一个失败的机会。亚马逊的巨大成功不是因为他们从来没有错,而是因为他们建立了一种文化,这种文化促进了创新,并为失败提供了安全的空间。他们是怎么做到的?
关键在于测试和实验,这是世界上最成功的公司,如脸书、谷歌、亚马逊和微软已经根植于他们的文化中的东西。他们向一小部分用户展示他们的最新想法。这就是“测试”组。与此同时,他们保留了一组“控制”或“安慰剂”用户,他们还没有经历任何变化。这组人可能不会改变他们的行为,但如果他们这样做了,你可以肯定这不是因为测试。它们提供了一个“基线”,根据这个基线可以衡量“测试”组中任何行为变化的增量效果。
一次又一次,这些公司成功了,因为他们很快失败了,并转向了新的东西。如果你的“测试”组对任何变化都没有反应,并且继续表现出与“控制”组一致的行为,那么你失败的程度要小得多。然而,你可能在一个无论如何都不会起作用的改变上节省了数百万美元。
“有对失败的恐惧,就会有失败。”乔治·S·巴顿将军
测试和实验一点都不新鲜。我们已经做了很多年了。一个很好的例子是进行临床试验,测试一种新药是否有效。直到最近,FAANG 公司才把这变成了一种商业哲学。为什么不呢?我们都知道他们是多么的成功。所以,我们不都应该开始做实验吗?什么会出错?
一个设计糟糕的测试会像不测试一样有效地毁掉你的生意。这是因为设计不良的测试会给出有偏见和不可靠的结果,提供误导性的信息,让你误以为是有用的见解。那我们如何拯救我们自己呢?正如任何批判性思考者会说的——提出问题!
- ****为什么你要进行这个测试?变革的预期效果是什么?
- ****什么样的指标能正确衡量变革的效果?
- 谁应该是你测试的一部分?
- 您的测试应该运行多长时间?
为什么?
变革的预期效果是什么?你想获得更高的广告收入吗?或者,你只是想维持用户参与度?这不是一个数据科学或分析问题:这是商业战略。与您的首席执行官和关键决策者谈论他们关心的指标是什么,以及测试有一个目标。
“如果你不知道要去哪里,你必须非常小心,因为你可能到不了那里。”—约吉·贝拉
你的“为什么”不需要总是关于未来。有时,您需要衡量刚刚发生的变化的效果。新冠肺炎一夜之间彻底改变了人们的消费习惯。从连锁杂货店、餐馆老板到服装零售商,每个人都只能眼睁睁地看着自己的企业要么被压垮,要么被摧毁。你无法挽回已经发生的事。然而,在新的现实中两个月后,你仍然可以从过去的事件中形成假设,用你所拥有的数据来测试它们,并利用你的发现来为未来做出明智的决定。
什么?
什么应该是可测量的效果:一个你想要跟踪的度量。您可以收集许多参与度指标,但并非所有指标都与您的变更相关。如果你想测量用户保持率,那么你需要测量你的日活跃、周活跃或月活跃指标。如果你想从广告中赚更多的钱,你最好测量一下你应用程序上广告印象的点击率。
有一个清晰定义的“什么”也将帮助你磨练测试中重要的部分,并使它在决策中分析和使用更清晰。你没有做广告测试来发现你的客户是否在不同的时间登录你的网站。但是,不要过犹不及,目光短浅。您可能希望在进一步开展计划之前,看到测试对您的一些 KPI 的影响。
谁啊。
最终,你对谁进行测试将对测试是否有用有很大的影响。需要记住业务和技术两方面的考虑,它们分为三类:
- 你的测试目标是什么类型的客户?
- 需要多大的样本量?
- 我应该如何划分测试和控制?
当你在考虑你的消费者时,你的目标应该是决定测试的范围,这样你可以得到更清晰和更有力的见解。您可以根据静态或行为属性对您的消费者进行细分。一个更小的有针对性的测试将证明比全面测试更可靠,即使是在你从未打算推出产品的领域。
心中有一个目标不仅对细分消费者很重要,对理解预期效果大小也很重要。一个更大的测试会给你更多的统计能力,让你可靠地测量一个更小的效果大小。请记住,您的测试越大,执行它的成本就越高。因此,您的样本大小应该由最小影响大小来决定,这个最小影响大小刚好足以让您可靠地测量变更的增量影响。
在某一点之后,真正重要的不是测试样本有多大,而是它对你的目标人群有多大的代表性。测试的黄金标准一直是随机化,但也有更微妙的策略可用。
多久了?
我们都喜欢快速反馈。你的实验设计也应该如此。当您考虑您的测试应该运行多长时间时,您的目标应该是尽可能快地获得洞察力。你的实验进行得越久,代价就越大,要么是在一次糟糕的测试中推出更多的客户,要么是更快推出一个好主意的机会成本。幸运的是,我们有统计技术来帮助优化实验的长度。例如,您可以使用一种带有反馈循环的多臂强盗方法,该方法经常呈现有效的测试版本,并避免那些无效的版本。一旦你开始得到非常令人信服的结果,表明测试正在工作,你可以应用有效停止测试。
最后,我们学到了什么?
"一个白痴讲的故事,充满了喧嚣和愤怒,却毫无意义!"——威廉·莎士比亚
用不朽的莎士比亚的话来说,有效的测试和实验都是关于“明天、明天、明天”的思考。你需要为你的公司想要做的事情建立一个长期的愿景,从而计划你的测试来开辟这条道路。为你的长期测试策略做计划,不仅包括基本的想法,还包括度量标准、样本大小和防止测试重叠。太多的公司要么不理解测试的价值,根本不做测试,要么他们疯狂地进行设计糟糕的测试,最终只是声音和愤怒,没有任何意义。确保你的测试意味着什么,并告诉你一个可操作的故事,你可以用它来指导业务。
使用 Pytest、Cython 和 spaCy 测试、分析和优化 NLP 模型
单元测试你的机器学习模型,剖析你的代码,充分利用 c 的自然语言处理速度。
目录:
- 为什么您应该关注分析 NLP 模型
- 为什么 spaCy 比 NLTK 快
- 当你调用 nlp(doc)时,到底发生了什么?
- 设置模型测试
- 判决结果:哪一方因速度而胜
- 编写文本处理管道的最佳实践
- NLP 优化的建议工作流程
- 资源和代码库
我读了一篇博文声称已经为自然语言数据预处理分析了 spaCy 和 NLTK,并且发现 NLTK 要快得多。
嗯(通过 giphy ,@ TheLateShow)
什么?
NLTK 是自然语言处理工具包中的吉普大切诺基:它很大,已经存在很长时间了,它有很大的动力,但它很慢,很费油。
SpaCy 是兰博基尼 Aventador。它可能不是一个有着所有花里胡哨的坦克,但它很光滑,只剩下裸露的金属。它做什么,它就做快。它的高马力也带来了风险(和乐趣),即在蜿蜒的道路上加速转弯时转弯过猛,偏离道路。
这个,但是用向量(via giphy ,@ thegrandtour)
所以,这个说法让我觉得很奇怪:
Spacy 比 NLTK 慢多了。NLTK 只用了 2 秒钟就标记了 7 MB 的文本样本,而 Spacy 用了 3 分钟!
如果您正确地编写了管道,并且您的硬件有足够的内存,这是不可能发生的。
我为什么要关心?
我主要从事研究工程——我的工作是制作模型,而不是构建软件架构。那么,如果你只想构建聊天机器人和搜索引擎,为什么要关心你的代码性能呢?
如果你对构建数据的最佳模型感兴趣,你不能脱离代码质量来考虑机器学习模型。
- 大规模的 NLP 语料库意味着,除非你尝试在空间和时间上优化你的代码,否则你将在算法和工具上受到限制。
- 不写测试意味着你不知道什么变化导致了什么错误——或者更糟,在你的实体模型中可能有你甚至没有意识到的问题,并且永远不会发现。(你是否曾经编写了一个 ML 函数或类,却发现它从未被调用过?)
- 理解您正在使用的库不仅对代码性能至关重要,而且对深入理解您正在构建的模型的实质也很重要。你将加速你的知识从简单的导入 scikit-learn 和装配一个估计器到真正的搜索支持 NLP 的算法。
- 良好的测试和深思熟虑的代码对于可重复的研究至关重要。
- 如果你是为了生产而写代码,而不仅仅是为了学术目的,你绝对需要考虑它在现实世界中如何运行。
为什么 spaCy 这么快?
SpaCy 的喷漆工作是 Python,但引擎是 Cython。 Cython 就像一种克里奥尔语言——一部分是 Python,一部分是 c。它是 Python 的超集(所有有效的 Python 都是有效的 Cython),但它包含了来自 c 的更快、更复杂的特性
对于某些用例,Cython 是一个很好的速度提升工具——例如,计算大量数字结果,其结果取决于一系列 if 语句。通常你会使用 numpy 来加速计算,但是 numpy 擅长于矢量化代码——对每个元素做同样的事情。当我们想要对每个元素做不同的事情时,Cython 允许我们使用相同的 c 优化,但是增加了逻辑复杂性。
SpaCy 广泛使用了开箱即用的 cythonization,与许多其他 Python 库相比,它是一个非常快速的 NLP 框架。
不理解你的算法的陷阱
让我们回到那个认为 spaCy 比 NLTK 慢的博客。
当我使用 line_profiler 在相同的文档上运行我的 NLTK 和 spaCy 文本清理器版本时,采用了苹果对苹果的比较, NLTK 花了 spaCy 5.5 倍的时间来做基本上相似的工作。
TL;DR: NLTK 在相同数据上的可比 NLP 管道中使用时比 spaCy 慢 5.5 倍。
我的代码有什么不同?首先,我从里到外研究了这些系统的架构,包括:
- 他们在做什么工作,什么时候做
- 他们期望并返回什么数据类型
- 功能的控制流
为了进行有意义的比较,您需要知道 spaCy 在被引用的代码中比 NLTK 做了更多的 lot 。默认情况下,spaCy 管道包括标记化、词汇化、词性标注、依存解析和命名实体识别。每次您调用nlp(doc)
时,它都会完成所有这些(甚至更多)。
spaCy 的词性标注和依存解析功能示例
相比之下,NLTK(在引用的代码中)只是标记和小写的语料库。
将这两条管道进行对比是荒谬的。
我们需要一个更好的框架:
- 我暂时禁用了 spaCy 管道中的依赖解析和命名实体识别,因为 NLTK 不做这些事情。
- 我让 NLTK 做了词性标注和词汇化,让它更接近 spaCy 的性能。
这仍然不是完美的一对一比较,但已经非常接近了。
两个麻雀的故事
我写了两个版本的 spaCy pipeline,来反映我是如何看到人们在野外使用nlp.pipe
的。第一个是list(nlp.pipe(docs))
。第二种更有效的方法是使用nlp.pipe(docs)
作为生成器对象。
用于相互比较管道的脚本。
github.com](https://github.com/lorarjohns/nlp_profiling/blob/master/scripts/profiling_spacy.py)
这种区别很重要,因为与 Python 列表不同,生成器对象不会同时在内存中保存整个语料库。这意味着您可以迭代地修改、提取或写入数据库的内容。在 NLP 中,这种情况经常出现,因为语料库通常非常大——大到无法一次保存在内存中。
结论是什么?
cProfile(戏剧化)(通过 giphy ,@ f1)
在同一个 Reddit 语料库(n=15000)上测试我的三个管道(使用 NLTK、spaCy-as-list 和 spaCy-as-generator),下面是 line_profiler 的结果:
- 列表空间:总时间= 64.260 秒
- 发电机空间:总时间= 60.356 秒
- NLTK:总时间= 334.677 秒
分析的结果可以在这里以*_reddit_2.txt 的形式找到](https://github.com/lorarjohns/nlp_profiling/tree/master/profiling)
使用 spaCy,甚至不用编写自己的 Cython,就可以将代码速度提高 5 倍以上。
这是有道理的——NLTK 没有用 Cython 进行优化,当它标记和 lemmatizes 令牌时,它使用了比 spaCy 更耗时的操作。
编写文本预处理管道的最好方法是什么?
与任何代码一样,如果你不理解 spaCy 的数据结构以及如何负责任地使用它们,spaCy将会变得很慢。**
阅读源代码。 SpaCy 依靠两个主要的东西来快速运行:Cython 的 c 内核和 Python 生成器。因此,它使用复杂的类和数据管道,这意味着它的对象的类型和方法并不总是显而易见的。
例如,当您在文本上调用nlp
来获取一个 Doc 对象时,spaCy 会生成 c 代码来处理您的数据。虽然您可以使用括号符号等普通命令进行索引以与结果文档进行交互,但它们的工作方式与基本 Python 不同。更确切地说,Doc 类重载了 Python 操作符,让您可以像与 Python 对象一样与结构进行交互。
Doc 类中的一个 c 函数
真正了解这一点的唯一方法是阅读源代码。文档不会为你解释清楚。除了通过 Spacy 教程学习 高级 NLP 之外,没有其他方法可以真正掌握诀窍。
轻松阅读源代码将极大地提高您的编程素养和数据管道。
有计划。在开始编写之前,确保你已经用涂鸦和伪代码概述了管道的步骤。有一个深思熟虑的计划将帮助你思考如何最好地将不同的功能联系在一起。您将看到数据的效率,将函数和变量组合在一起以加快速度的方法,以及如何转换数据以方便流动。
自然语言处理优化的工作流程
- 解决问题不考虑最优性。让它工作起来。
- 在你开始改变你的代码之前,准备好工作测试。拥有测试意味着你可以改变架构的重要部分,而不用担心引入新的神秘错误。测试并不是很多数据科学家预先考虑的事情,而且考虑如何为 NLP 机器学习模型编写测试并不总是容易的。但是至少在数据预处理方面,它们可以帮助您避免预处理不好的文本的“垃圾输入,垃圾输出”现象。一些例子包括:
- 为你的正则表达式(例如,你用来标记句子的表达式)编写单元测试,确保它们只匹配你想要的
- 检查你的函数是否返回正确的数据类型。返回值应该是数字数组、字典、字符串、列表还是列表列表?测试一下。
- 检查你所有的类和函数是否真的按照你想要的方式被调用。发现重要的嵌入模型或特征工程组件得到了错误的输入(或者更糟,根本没有被调用)一点都不好玩。
但是,在开始 cythonizing 之前,请确保您已经剖析了代码并清理了 Python。甚至像 gensim 这样的纯 Python 库也可以非常快,而且内存效率高,因为它们严格地收紧了数据结构和管道。
- 持续地、可重复地测量代码的速度和性能。你需要一个基线来知道你是否在改善(或者让事情变得更糟)。行分析器、 cProfile 和 py-spy 都会生成记录脚本性能的文件,您可以阅读、引用和比较这些文件。
- 确保你使用了正确的数据结构和函数调用。在你确定你的 Python 已经尽可能快之前,不要为 Cython 费心。有时候,Cython 并不是合适的工具,对 Python 代码的微小修改会比 cythonization 产生更大的改进。正如我们所见,用列表替换生成器是一个简单的瓶颈修复方法。
- 检查你的函数的控制流。逐行分析器在这里真的很有帮助。在编写比较时,我注意到 line_profiler 说我的 spaCy 函数在文本清理函数的 NLTK 部分花费了时间。我已经编写了代码,以便在我检查
spacy=True
参数是真还是假之前定义只与 NLTK 相关的内部函数。将该代码移动到 if-else 语句的 NLTK 部分中,可以阻止我的 spacy 模型每次都运行该(不相关的)代码块。 - 编写好的测试。考虑你的函数需要产生什么样的输出来通过你的测试,几乎会迫使你自动去做 1-3。
使用 pytest 对管道进行的一些测试
github.com](https://github.com/lorarjohns/nlp_profiling/blob/master/scripts/test_profiling_spacy.py)
更好的代码,更好的科学
以这种方式思考需要对您的模型进行更多的架构设计,但是为了避免等待半个小时来处理您的文档,这是值得的。花时间仔细考虑模型的逻辑结构,编写测试,并思考分析器的输出,以避免以即时满足的名义进行草率的科学研究。任何人都可以制造一袋单词;NLP 的精妙之处在于细节工作。
资源:
- 我对这篇博文的完整回购,附带理解 spaCy 内部结构和编写自己的 Cython 的额外代码
github.com](https://github.com/lorarjohns/nlp_profiling)
使用 SeleniumBase 测试简化应用
在我为 Streamlit 工作的这段时间里,我见过数百款令人印象深刻的数据应用,从计算机视觉应用到公共卫生新冠肺炎的追踪,甚至是简单的儿童游戏。我相信 Streamlit 越来越受欢迎的原因是通过 Streamlit“魔法”功能和保存 Python 脚本时自动重新加载前端的快速迭代工作流。编写一些代码,在编辑器中点击“保存”,然后直观地检查每个代码更改的正确性。随着用于简化部署 Streamlit 应用程序的 Streamlit sharing 的推出,您可以在几分钟内从构思到编码再到部署您的应用程序!
一旦您创建了一个 Streamlit 应用程序,您就可以使用自动化测试来保证它不会出现倒退。在这篇文章中,我将展示如何使用 Python 包 SeleniumBase 以编程方式验证 Streamlit 应用程序在视觉上没有改变。
案例研究:细流叶
streamlit-flour 测试应用程序的基准图像(作者提供的图像/截图)
为了演示如何创建自动化的可视化测试,我将使用streamlit-leave GitHub repo,这是我为 leave . js 的leave Python 库创建的 Streamlit 组件。视觉回归测试有助于检测应用的布局或内容何时发生变化,而不需要开发人员在其 Python 库中每次代码行发生变化时手动视觉检查输出。视觉回归测试还有助于提高您的 Streamlit 应用程序的跨浏览器兼容性,并提供有关影响应用程序显示方式的新浏览器版本的高级警告。
设置测试工具
streamlit-follow 测试工具有三个文件:
[tests/requirements.txt](https://github.com/randyzwitch/streamlit-folium/blob/master/tests/requirements.txt)
:测试只需要 Python 包[tests/app_to_test.py](https://github.com/randyzwitch/streamlit-folium/blob/master/tests/app_to_test.py)
:参考要测试的 Streamlit 应用[tests/test_package.py](https://github.com/randyzwitch/streamlit-folium/blob/master/tests/test_package.py)
:演示包按预期工作的测试
第一步是使用要测试的包创建一个 Streamlit 应用程序,并使用它来设置基线。然后,我们可以使用 SeleniumBase 来验证应用程序的结构和视觉外观相对于基线保持不变。
这篇文章的重点是描述test_package.py
,因为它是涵盖如何使用 SeleniumBase 和 OpenCV 进行 Streamlit 测试的文件。
定义测试成功
从测试的角度来看,有几种方法可以思考什么构成了看起来相同的。我选择了以下三个原则来测试我的 streamlit-leav 包:
- 页面的文档对象模型(DOM)结构(但不一定是值)应该保持不变
- 对于标题等值,测试这些值是否完全相等
- 视觉上,应用程序应该看起来一样
我决定采用这些不太严格的“未更改”定义来测试 streamlit-follow,因为 follow 包本身的内部似乎是不确定的。这意味着,相同的 Python 代码将创建相同的外观的图像,但是生成的 HTML 将会不同。
使用硒基测试
SeleniumBase 是用 Python 编写的一体化框架,它包装了用于浏览器自动化的 Selenium WebDriver 项目。SeleniumBase 有两个函数可以用于上面列出的第一个和第二个测试原则: check_window ,它测试 DOM 结构和 assert_text ,以确保页面上显示特定的一段文本。
为了检查 DOM 结构,我们首先需要一个基线,我们可以使用check_window
函数生成这个基线。根据所需的name
参数,check_window
有两种行为:
- 如果在
visual_baseline/<Python file>.<test function name>
路径中不存在文件夹<名称> 的话,这个文件夹将由所有的基线文件创建 - 如果文件夹确实存在,那么 SeleniumBase 将按照指定的精度级别将当前页面与基线进行比较
您可以在 streamlit-leav repo 中看到一个调用 check_window 和结果基线文件的示例。为了在两次运行之间保持基线不变,我将这些文件提交给了 repo 如果我要对我正在测试的应用程序(app_to_test.py
)进行任何实质性的更改,我需要记住设置新的基线,否则测试将会失败。
基线文件夹现在已经存在,运行 check_window 将运行比较测试。我选择在级别 2 运行测试,级别定义如下:
- 级别 1(最不严格) : HTML 标签与 tags_level1.txt 进行比较
- 级别 2 : HTML 标签和属性名与标签 _ 级别 2.txt 进行比较
- 第三级(最严格) : HTML 标签、属性名和属性值与 tags_level3.txt 进行比较
正如在“定义测试成功”一节中提到的,我在第 2 级运行了check_window
函数,因为 follow 库向 HTML 中的属性值添加了一个类似 GUID 的 ID 值,所以测试永远不会在第 3 级通过,因为属性值在运行之间总是不同的。
对于第二个测试原则(“检查某些值是否相等”),使用assert_text
方法非常容易:
self.assert_text("streamlit-folium")
该函数检查应用程序中是否存在准确的文本“streamlit-follow ”,测试通过,因为在本例中这是 H1 标题的值。
使用 OpenCV 进行测试
虽然检查 DOM 结构和一段文本的存在提供了一些有用的信息,但我真正的接受标准是应用程序的视觉外观不会从基线改变。为了测试应用的视觉效果是否与像素相同,我们可以使用 SeleniumBase 的save_screenshot
方法来捕捉应用的当前视觉状态,并使用 OpenCV 包与基线进行比较:
使用 OpenCV,第一步是读入基线图像和当前快照,然后比较图片的大小是否相同(shape
比较检查像素的 NumPy ndarrays 是否具有相同的尺寸)。假设图片大小相同,我们可以使用 OpenCV 中的subtract
函数来计算每个通道(蓝色、绿色和红色)像素之间的差异。如果所有三个通道没有差异,那么我们知道 Streamlit 应用程序的视觉表示在运行之间是相同的。
使用 GitHub 动作自动化测试
随着我们的 SeleniumBase 和 OpenCV 代码的设置,我们现在可以自由地对我们的 Streamlit 组件(或其他 Streamlit 应用程序)进行更改,而不用担心意外中断。在我的单贡献者项目中,很容易在本地强制运行测试,但是有了开源项目免费提供的工具,如GitHub Actions,建立一个持续集成管道保证了每次提交都运行测试。
streamlit-leav 定义了一个工作流程[run_tests_each_PR.yml](https://github.com/randyzwitch/streamlit-folium/blob/master/.github/workflows/run_tests_each_PR.yml)
,执行以下操作:
- 为 Python 3.6、3.7、3.8 建立一个测试矩阵
- 安装包依赖关系和测试依赖关系
- 将代码用薄片 8 标记
- 用硒鼓安装铬合金
- 运行 Streamlit app 进行后台测试
- 在 Python 中运行 SeleniumBase 和 OpenCV 测试
通过在 repo 中定义这个工作流,并在 GitHub 上启用所需的状态检查,现在每个 pull 请求都会在底部附加以下状态检查,让您知道您的更改的状态:
合并前 GitHub 运行的检查(图片/作者截图)
从长远来看,编写测试可以节省工作
在代码库中进行测试有很多好处。如上所述,自动化视觉回归测试允许你维护一个应用程序,而不需要一个人在循环中寻找变化。编写测试对潜在用户来说也是一个很好的信号,表明你关心项目的稳定性和长期可维护性。为 Streamlit 应用程序编写测试并让它们在每次 GitHub 提交时自动运行不仅容易,而且从长远来看,向 Streamlit 项目添加测试的额外工作将节省您的时间。
对本文或 Streamlit 有任何疑问吗?驻足于 Streamlit 社区论坛 ,展开讨论,结识其他 Streamlit 爱好者,在 Streamlit 组件跟踪器 或 中找到合作者,分享你的 Streamlit 项目 !有很多方法可以加入 Streamlit 社区,我们期待着您的到来🎈
原载于 2020 年 11 月 13 日https://blog . streamlit . io。
测试您的数据库
数据库过程测试的检查点概要
格伦·卡斯滕斯-彼得斯在 Unsplash 上拍摄的照片
无论在哪个行业,数据库测试都是任何数据系统实现的重要组成部分。实现一个供各种角色的员工日常交互的数据库,要求所有存储和检索的数据(无论是通过 SQL 查询还是某种 UI)都是准确的,否则数据库本身就毫无价值。创建数据库后,对其进行功能测试以确保应用程序的健壮性是至关重要的。测试将确保模式、表、触发器和数据完整性本身都是一致的,并且每个操作都是有效的。
主要有三种类型的数据库测试:
- 结构测试
- 功能测试
- 非功能测试
结构测试
验证数据存储库中主要不是供最终用户使用的所有元素。这一步涉及数据库组件,如服务器和其他数据存储组件,如列测试、模式测试、存储过程/视图测试和触发器测试。
模式测试(映射测试)
模式本质上是我们数据库的映射,它为实际的查询过程提供了重要的指导,使我们能够获取准确的数据。模式测试将允许我们确保应用程序的后端和前端的模式映射是相似的。
当进行模式测试时,我们必须确保:
- 模式已经过验证—表的映射格式必须与最终用户界面的映射格式兼容。
- 如果一个环境包含多个数据库,这些数据库需要与应用程序的整体映射保持一致
- SQL Server 允许测试人员通过简单和独立的查询来查询数据库的模式。
示例:如果我们有一个包含一组表的模式,那么对单个表的结构所做的更改应该有效地适应所有依赖于该更改后的表的结果查询。
数据库/列测试
在这个测试步骤中,我们必须验证数据库及其特性列的结构是一致的。
- 字段长度和命名约定必须在应用程序的所有级别(后端和用户界面)上匹配
- 必须考虑任何未使用或未映射的表和列,并验证它们的存在
- 应用程序后端的数据类型和数据长度必须能够为最终用户提供高效、正确的摘要视图
- 必须适应用户的输入文档——换句话说,应用程序必须能够支持用户的业务规范
- 必须根据域要求定义主键和外键,并且必须包含在各自的表中(此外,必须考虑到键的任何唯一或非空特征)
触发测试
当你想到“触发”时,想想连锁反应。触发测试确保数据库代码的过程流正常工作,没有错误。
- 遵循标准编程编码标准
- 执行的触发器匹配所需的条件
- 触发器应该正确地更新数据(更新、插入、删除等)。)
数据库服务器检查
这些测试都围绕着主机服务器管理它将收到的请求的能力。
- 每个业务域的预期事务数量将由服务器提供
- 必须根据业务需求维护用户授权标准
存储过程和视图
这个步骤本质上验证了当用户执行查询或界面请求时,将为用户检索并显示正确的结果。
- 异常/错误得到正确处理,不会破坏系统
- 遵循传统的编码标准
- 接受任何条件或循环(输入数据通过并被接受)
- 任何未使用的过程都被考虑在内
- 验证存储过程模块是否正常工作,并在数据库中流动
功能测试
这个测试点要求最终用户将要执行的任何和所有交易或操作与业务的任何要求一致,并且它们都可以被有效和准确地执行。
卢卡·布拉沃在 Unsplash 上的照片
- 字段必须声明为强制的,也可以不声明,并且可以处理潜在的空值
- 每个字段的长度必须是适当的大小
- 所有表中的字段标题必须相同,以避免查询时混淆
黑箱测试
这种类型的测试需要验证数据库集成以检查功能。这些测试将验证来自用户的数据以及该功能产生的任何数据的完整性。一些黑盒测试的例子:
- 因果图
- 等价划分
- 边界值分析
白盒测试
这些测试集中于用户看不到的数据库内部结构。数据库中的模块与触发器、视图和一些查询一起被强调。数据库中的表、模式和模型将使用默认表值进行验证,以验证整个数据库的一致性。在这个过程中,代码中的 bug 和其他错误通常会被发现和协调。白盒测试的一些例子:
- 条件覆盖
- 决策覆盖
- 报表覆盖范围
非功能测试
非功能测试本质上是我们数据库的性能测试。完成这些测试是为了符合业务需求,这将有助于了解实际结果是否与预期结果一致。在这一步中,要考虑压力条件下的反应时间等因素。
阿洛拉·格里菲斯在 Unsplash 上拍摄的照片
负载测试
这种类型的测试将检查是否有任何正在运行的事务对数据库的整体性能有影响。
- 多个最终用户一次执行一个事务的响应时间应该同样一致
- 系统中最重要的功能应该进行最多的测试
- 应包含可编辑的事务,以区别于不可编辑的事务
- 应注意最佳响应时间
负载测试的示例:
- 一遍又一遍地运行一个非常常见的查询事务,以确保一致性
- 从外部来源添加大量数据
- 在同一台计算机或服务器上同时运行多个应用程序,以确保应用程序不受影响
压力测试
在压力测试中,我们试图让应用程序的工作负载过载,以确定故障点(断点)。然后,可以针对这些要点进行改进,或者向最终用户突出显示。通常,会使用第三方压力测试工具。
例如,如果建议一个 ERP 应用程序在任何给定时间最多接受 20,000 个用户,压力测试人员将逐渐增加工作负载,使其超过最大值,以确定应用程序在哪个点开始出现故障。在为最终用户组织文档时,可以考虑这些基准。
包扎
执行数据库测试的方法有很多,通常公司会让员工单独负责测试数据。理解和使用这些过程将允许任何数据用户创建和使用健壮的数据库应用程序,满足业务和最终用户的所有功能需求。
用 GitHub 动作测试您的 Python 项目
几天前,我第一次在我的一个项目中使用 GitHub Actions。总的来说,结果是非常积极的,但也有一些工作要做。希望这篇文章能帮助你以更低的成本做到这一点!
激活 GitHub 动作
在项目的根目录下创建一个.github/workflows
目录:
mkdir -p .github/workflows
在该目录中,用您选择名称创建一个或多个 YAML 文件。
在我的例子中,我创建了两个文件:
[continuous-integration-pip.yml](https://github.com/mwouts/jupytext/blob/master/.github/workflows/continuous-integration-pip.yml)
,以及[continuous-integration-conda.yml](https://github.com/mwouts/jupytext/blob/master/.github/workflows/continuous-integration-conda.yml)
。
命名您的行动
在 YAML 文件的顶部,我们有这个字段:name: CI (pip)
。虽然 YAML 文件的名称似乎没有任何影响,但文件顶部的name
字段是出现在您的操作选项卡中的字段,也是您将在工卡上看到的名称:
使用on
字段,您可以选择哪些事件应该触发一个动作。我最初尝试了on: [push, pull_request]
,但是我很快就感觉触发了太多的构建。现在我使用on: [push]
,这似乎就足够了。这样,当贡献者的一个提交破坏了配置项时,他们会收到一封电子邮件,因此他们可以在打开拉请求之前修复问题。使用on: [push]
,您已经在 pull 请求中获得了状态更新:
几分钟后:
测试 Python 的多个变体
Python 代码是可移植的,但实际上,我更喜欢在各种平台上测试我的代码。
在我的项目中,我想测试
- Python 环境使用
pip
和conda
构建 - Python 版本 2.7、3.5、3.6、3.7 和 3.8
- Linux、Mac OS 或 Windows 上的 Python。
注意,我不需要测试完整的矩阵(30 个作业)。如果我在所有可能的操作系统(11 个作业)上测试了 pip 和 Linux 的所有 Python 版本,以及 conda 的 Python 2.7 和 3.7,那么我已经可以确信我的程序将在几乎所有地方工作。
先说pip
,超级容易设置。这里是我的[continuous-integration-pip.yml](https://github.com/mwouts/jupytext/blob/master/.github/workflows/continuous-integration-pip.yml)
文件的摘录,显示:
- 如何使用
actions/checkout@v2
查看 GitHub 库 - 如何在
python-version: ${{ matrix.python-version }}
参数化的版本中用actions/setup-python@v1
安装 Python - 如何从项目的
requirements.txt
和requirements-dev.txt
文件安装包 - 如何安装可选依赖项
name: CI (pip)
on: [push]
jobs:
build:
strategy:
matrix:
python-version: [2.7, 3.5, 3.6, 3.7, 3.8]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt
# install black if available (Python 3.6 and above)
pip install black || true
现在我们前往conda
。我的[continuous-integration-conda.yml](https://github.com/mwouts/jupytext/blob/master/.github/workflows/continuous-integration-conda.yml)
文件摘录显示:
- 如何用
actions/checkout@v2
检查你的 GitHub 库 - 如何在 Ubuntu (Linux)、Mac OS、Windows 中选择操作系统
- 如何用
goanpeca/setup-miniconda@v1
安装 Miniconda,用python-version: ${{ matrix.python-version }}
参数化的 Python 版本 - 如何从
[environment.yml](https://github.com/mwouts/jupytext/blob/master/environment.yml)
文件创建 conda 环境 - 以及如何激活相应的环境
name: CI (conda)
on: [push]
jobs:
build:
strategy:
matrix:
os: ['ubuntu-latest', 'macos-latest', 'windows-latest']
python-version: [2.7, 3.7]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup Miniconda
uses: goanpeca/setup-miniconda@v1
with:
auto-update-conda: true
auto-activate-base: false
miniconda-version: 'latest'
python-version: ${{ matrix.python-version }}
environment-file: environment.yml
activate-environment: jupytext-dev
代码质量
在运行任何测试之前,确保所有代码都是有效的是一个好主意,我用flake8
做到了这一点。
我的[continuous-integration-pip.yml](https://github.com/mwouts/jupytext/blob/master/.github/workflows/continuous-integration-pip.yml)
文件中对应的步骤是
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# all Python files should follow PEP8 (except some notebooks, see setup.cfg)
flake8 jupytext tests
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --statistics
在[continuous-integration-conda.yml](https://github.com/mwouts/jupytext/blob/master/.github/workflows/continuous-integration-conda.yml)
中,需要一个额外的shell
参数。我第一次尝试shell: pwsh
(PowerShell),效果不错,所以我现在在用
- name: Lint with flake8
shell: pwsh
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# all Python files should follow PEP8 (except some notebooks, see setup.cfg)
flake8 jupytext tests
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --statistics
了解 CI 错误
我最初对flake8
步骤有疑问。错误是:
Lint with flake8
4s
##[error]Process completed with exit code 2.
Run # stop the build if there are Python syntax errors or undefined names
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
all Python files should follow PEP8 (except some notebooks, see setup.cfg)
flake8 jupytext tests
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --statistics
shell: /bin/bash -e {0}
env:
pythonLocation: /opt/hostedtoolcache/Python/3.6.10/x64
0
/home/runner/work/_temp/98d1db20-f0af-4eba-af95-cb39421c77b0.sh: line 3: syntax error near unexpected token `('
##[error]Process completed with exit code 2.
我的理解是,/home/runner/work/_temp/98d1db20-f0af-4eba-af95-cb39421c77b0.sh
是一个临时脚本,包含该步骤的命令。所以当我们被告知脚本抛出这个错误:第 3 行:意外标记(*)附近的语法错误时,我们应该看看那个步骤的
run`属性的第三行。在我的命令中,那是所有的 Python 文件都应该遵循 PEP8(除了一些笔记本,见 setup.cfg)* ,而且确实是少了一个注释 char!
运行 Pytest
一旦我们确信代码在语法上是正确的,我们就想知道是否所有的单元测试都通过了。相应的步骤是:
- name: Test with pytest
run: coverage run --source=. -m py.test
注意,我使用coverage run --source=. -m py.test
而不仅仅是pytest
,因为我也想知道代码覆盖率。还有,对于 conda 文件,我们需要添加一个shell: pwsh
属性,否则找不到coverage
或者pytest
命令。
上传覆盖范围
在配置项中计算覆盖率是很好的,但是在拉请求中更新覆盖率,并在自述文件中显示覆盖率标记就更好了。为此,我使用 codecov 。我更喜欢只上传 conda CI 的覆盖率,因为它让我可以测试更多的可选功能。覆盖率上传步骤是[continuous-integration-conda.yml](https://github.com/mwouts/jupytext/blob/master/.github/workflows/continuous-integration-conda.yml)
中的最后一步:
- name: Upload coverage
shell: pwsh
run: coverage report -m
有了这个,我就有了保险徽章
我可以添加到我的自述文件中,详细的覆盖率统计数据和图表在 codecov.io 中,以及拉请求中的覆盖率报告:
自动化作业取消
一开始让我感到惊讶,但很有意义的一个特性是自动作业取消。当 CI 中的一个作业失败时,所有其他仍在运行或挂起的作业都会被取消。
下面是我的项目中的一个例子——Windows 版本出现了一个问题,导致剩余的 Windows 和 Mac OS 作业被取消:
与 Travis-CI 的差异
在发现 GitHub 动作之前,我使用的是 Travis-CI 。我喜欢它!我在许多项目中使用过 Travis-CI,包括这个奇特的项目,在那里我们测试…自述文件本身!
现在,我将以两者之间的简单比较来结束我的发言。
- 特拉维斯. CI 在那里的时间要长得多。我可能担心 GitHub 操作的文档会更难找到,但事实并非如此。有用的参考有:使用 Python 和 GitHub 动作和设置 Miniconda 。
- 就像 Travis-CI 一样,Github Actions 可以免费用于公共项目。
- GitHub 集成对于 Travis-CI 和 GitHub 操作来说都非常出色,尽管这些操作在项目页面上有一个专用的选项卡,这使得访问 CI 更加容易。
- 用 Github 动作配置 conda 比用 Travis-CI 更简单,为此我使用了这个 hack 。
- 我发现这个矩阵(比如 Python 版本乘以 OS,比如这里的)在 Github 动作上使用起来更简单。
- 我没有注意到工作持续时间的显著差异。在我的示例 PR 中,Travis-CI 上的六个作业(pip 和 conda,仅 Linux)在 8m 33s 中运行,而在 GitHub Actions 上,第一系列的五个 pip 作业(仅 Linux)在 4m 57s 中运行,而其他系列的六个 conda 作业(Linux、Mac OS、Windows)在 12m 48s 中并行运行——因此,在该示例中,GitHub Actions 花费了 50%的时间,但也覆盖了更多的数量
感谢你阅读这篇文章!如果你想了解更多,可以看看我在 Medium 上的其他帖子,在 GitHub 上我的开源项目,或者在 Twitter 上关注我。此外,如果您有在其他环境中使用 GitHub 操作的经验,例如,如果您知道如何在线发布文档,请在此处发表评论让我们知道,或者随时就此回购提出问题或请求!
德州扑克模拟器
德州扑克是当今最流行的扑克游戏。随着社交距离协议调整了生活方式,许多人开始与朋友玩休闲游戏。失败一点也不好玩,有时这个游戏看起来像是对你不利。在本文中,我将讨论与玩不同牌局相关的概率,以及一个交互式应用程序,您可以使用它来模拟数千手扑克,以确定概率并提高您的技能。如果您对 Dash 应用感兴趣,请点击此处。
随着社交距离协议的继续,德州扑克的在线人数大幅增加。下图显示了随着冠状病毒开始影响美国,德州扑克应用程序 Poker Face 的增长情况。
我发现德州扑克中的一些数字知道起来很有用,也很容易计算。假设你坐在牌桌上,拿着一张红桃 j 和一张红桃 4,翻牌圈又出现了两张红桃。你现在在等一个有九张听牌的同花听牌。
在一副牌中,十三张有红心的牌,其中四张放在桌子上。剩下的 47 张牌中有 9 张可以提供最后一张红心给你同花。在还剩两张牌的情况下,凑成同花的概率约为 35%。
这是了解你的风险和决定你愿意赌多少来看下一张牌的有用信息,但它留下了许多未知数。在上面的场景中,玩家有一张高同花,尤其是因为牌桌上有红心王。别人有红桃皇后或 a 和另一张红桃牌的概率是多少?或者,他们只能有红心皇后或 a,并希望有两个以上的心显示。这取决于手中剩下的玩家数量,从而变得更加复杂。如果你要单挑,你可以相当有信心拿到高同花顺听牌,但如果你和其他几个玩家在一张牌桌上,你就没那么有信心了。也就是说,你仍有 65%的机会没有拿到同花。如果你打了一个 q,a,或者 q,9 或者 7,9(所有这些都可以按任意顺序打)。这里打顺子的概率是 4.4%。
计算特定场景的概率并不太难,但是对每一种可能的纸牌组合进行计算就变得非常复杂了。
这就是可以实现编程能力的地方。使用 python,您可以模拟数百万手牌,记录任何牌和牌桌组合的每次同花、顺子等等。这使得用户能够完全控制卡片,而不必进行任何计算。下面是之前讨论的 100 万手模拟牌的输出。
代码创建手牌,然后洗牌。首先,它将牌分发给一些选定的玩家。然后,它将剩余的 X 张牌放在桌上,并记录每手牌是否被击中。然后将该手牌与其他玩家的手牌进行比较,以确定该手牌是否是获胜的手牌,并记录该信息。这个过程重复 n 次模拟。随着模拟牌局数量的增加,所列概率的准确性也会增加。如前所述,我创建了一个 Dash 应用程序,你可以在这里亲自尝试一下。下面是应用程序的截图。
有了模拟手的能力,我们可以很容易地分析许多不同的情况。下图显示了每对口袋对子的获胜概率。有些差异是必需的,因为数十亿手牌的组合是可能的,而模拟只有数百万次。然而,你可以看到一个明显的趋势,毫不奇怪地表明,口袋二的远不如口袋火箭。
1.口袋 a 有多好?
2.7 2 异花到底有多烂?
3.你拿到同花 8-9 的顺子、同花或同花的可能性有多大?
与这个项目相关的所有代码都可以在我的 Github 上找到。如果你对这个项目有任何疑问,请随时联系我们。感谢您的阅读,希望您喜欢!
德州重新开放了。他们的新冠肺炎数据是怎么回事?
让我们快速看一下数据
恩里克·马西亚斯在 Unsplash 上的照片
自从德克萨斯州州长格雷格·阿博特宣布德克萨斯州的一些企业现在可以以 25%的能力开业以来,不到两周就过去了,包括零售店、图书馆、餐馆、商场、电影院和博物馆。上周一(18 日),他宣布进一步减少限制。健身房、儿童护理中心、办公楼、青年俱乐部、非必需制造业、按摩护理中心和工作办公室被允许立即重新开放。5 月 22 日,餐馆将被允许从 25%的容量增加到 50%的容量。同时,酒吧、品酒室和手工酿酒厂将被允许以 25%的容量开放。
在听取了所有这些限制后,重要的是要考虑重新开放的前一阶段已经产生了什么影响。
得克萨斯州卫生部周六报告称,该州共有超过 47000 例新冠肺炎确诊病例,仅周六一天就有超过 1800 例新增病例(见下图)。
每日新病例
得克萨斯州每日确诊新新冠肺炎病例(来源: Knowi 冠状病毒数据中心
在这张图表中,您可以看到每天新增病例的数量在稳步增加。随着大约两周前重新开放,这种增长可能会更快,这被认为是新新冠肺炎病例的潜伏期。
然而,漫长的潜伏期使得现有的上升趋势很难归因于重新开放本身。
5 月 16 日的大峰值是州长归因于增加测试的峰值。
测试增加
(来源: Knowi COVID 测试仪表板)
州长阿博特表示,得克萨斯州正在增加新冠肺炎检测的数量,特别是在疗养院、监狱和肉类加工厂等高风险设施中。这是最近报告的确诊病例增加的原因。
按日期在德克萨斯州进行的测试数量(来源: Knowi COVID 测试仪表板
尽管医学专家一再强调进行更多测试的重要性,但这确实使数据更难解读。数字上升是因为重新开放还是因为测试增加了?
德克萨斯州阳性检测的百分比(来源)
正如州长所指出的,该州阳性检测结果的百分比似乎呈下降趋势。而在 4 月初,阳性病例约为 10%,现在显示约为 5%。
然而,这可能并不像许多人似乎暗示的那样,实际上是感染人数减少的指标。以前在大多数州,接受测试的人是表现出最严重症状的人。随着各州对普通人群进行越来越多的检测,预计感染者的比例将会下降,因为接受检测的人不仅仅是那些表现出最明显新冠肺炎症状的人。
德克萨斯州的热点在哪里?
得克萨斯州各县新冠肺炎病例热图(来源:已知冠状病毒数据中心
正如你在上面的热图中看到的,得克萨斯州新冠肺炎病例最多的是两个最大的县:达拉斯县和哈里斯县。考虑到哈里斯县包括休斯顿市,这并不奇怪。得克萨斯州有 2100 多个病例发生在波特县,兰德尔县有 600 多个已知的新冠肺炎病例。少数病例分散在整个州。
埃尔帕索的官员甚至要求将他们排除在下一阶段重新开放之外。
前景尚不明朗
在这里,我简要介绍了与得克萨斯州新冠肺炎病例相关的数据。我不认为有足够的信息对将要发生的事情,甚至对现在正在发生的事情做出任何明确的预测。但我确实认为,阳性病例百分比下降表明危险减少的论点在逻辑上是有缺陷的,可能会误导人们,可能包括州长艾伯特。
Python 中的文本分析基础
入门
二元/三元模型、情感分析和主题建模
来源:https://unsplash.com/photos/uGP_6CAD-14
本文讨论 Python 中最基本的文本分析工具。我们不打算进入花哨的 NLP 模型。只是最基本的。有时候你需要的只是基础:)
我们先来获取一些文本数据。这里有一份我写的课程评论清单。我们可以用这些数据做什么?想到的第一个问题是,我们能分辨哪些评论是正面的,哪些是负面的吗?我们可以对这些评论做一些情感分析吗?
corpus = [
'Great course. Love the professor.',
'Great content. Textbook was great',
'This course has very hard assignments. Great content.',
'Love the professor.',
'Hard assignments though',
'Hard to understand.'
]
情感分析
太好了,我们来看看整体的情绪分析。我喜欢使用熊猫数据框。因此,让我们从列表中创建一个熊猫数据框。
import pandas as pd
df = pd.DataFrame(corpus)
df.columns = ['reviews']
接下来,让我们安装库 textblob ( conda install textblob -c conda-forge
)并导入库。
from textblob import TextBlob
df['polarity'] = df['reviews'].apply(lambda x: TextBlob(x).polarity)
df['subjective'] = df['reviews'].apply(lambda x: TextBlob(x).subjectivity)
然后,我们可以通过polarity
函数计算情感。polarity
范围从-1 到 1,其中-1 为负,1 为正。TextBlob 还可以使用subjectivity
函数来计算subjectivity
,范围从 0 到 1,0 表示客观,1 表示主观。
二元/三元模型的情感分析
接下来,我们可以探讨一些单词联想。n 元语法分析通常用于查看哪些单词经常一起出现。我经常喜欢研究两个词或三个词的组合,即二元模型/三元模型。
一个 n -gram 是来自给定文本或语音样本的 n 项的连续序列。
在文本分析中,过滤掉一些停用词通常是一种好的做法,这些停用词是最常见的词,但在句子中没有重要的上下文意义(例如,“a”、“the”、“and”、“but”等)。nltk 为我们提供了一个停用词的列表。我们还可以在列表中添加定制的停用词。比如这里我们加了“虽然”这个词。
from nltk.corpus import stopwords
stoplist = stopwords.words('english') + ['though']
现在,我们可以删除停用词,使用一些二元/三元模型。函数CountVectorizer
“将文本文档的集合转换成令牌计数的矩阵”。stop_words
参数有一个内置选项“英语”。但是我们也可以使用用户定义的停用词,就像我在这里展示的。ngram_range
参数定义了我们对哪些 n 元语法感兴趣——2 表示二元语法,3 表示三元语法。另一个值得一提的参数是lowercase
,它有一个默认值 True ,为我们自动将所有字符转换成小写。现在用下面的代码,我们可以得到所有的二元/三元模型,并按频率排序。
from sklearn.feature_extraction.text import CountVectorizer
c_vec = CountVectorizer(stop_words=stoplist, ngram_range=(2,3))
# matrix of ngrams
ngrams = c_vec.fit_transform(df['reviews'])
# count frequency of ngrams
count_values = ngrams.toarray().sum(axis=0)
# list of ngrams
vocab = c_vec.vocabulary_df_ngram = pd.DataFrame(sorted([(count_values[i],k) for k,i in vocab.items()], reverse=True)
).rename(columns={0: 'frequency', 1:'bigram/trigram'})
类似于之前的情感分析,我们可以计算每个二元/三元模型的极性和主观性。
df_ngram['polarity'] = df_ngram['bigram/trigram'].apply(lambda x: TextBlob(x).polarity)
df_ngram['subjective'] = df_ngram['bigram/trigram'].apply(lambda x: TextBlob(x).subjectivity)
主题建模
我们也可以用文本数据做一些主题建模。有两种方法可以做到这一点:NMF 模型和 LDA 模型。接下来我们将展示使用这两种方法的例子。
NMF 模型
非负矩阵分解(NMF)是一种矩阵分解方法,将一个矩阵分解为非负元素 W 和 H 的乘积。默认方法优化原始矩阵和 WH 之间的距离,即 Frobenius 范数。下面是一个例子,我们使用 NMF 产生 3 个主题,我们在每个主题中显示了 3 个二元模型/三元模型。
Source: [https://scikit-learn.org/stable/auto_examples/applications/plot_topics_extraction_with_nmf_lda.html](https://scikit-learn.org/stable/auto_examples/applications/plot_topics_extraction_with_nmf_lda.html)from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import NMF
from sklearn.pipeline import make_pipelinetfidf_vectorizer = TfidfVectorizer(stop_words=stoplist, ngram_range=(2,3))
nmf = NMF(n_components=3)
pipe = make_pipeline(tfidf_vectorizer, nmf)
pipe.fit(df['reviews'])def print_top_words(model, feature_names, n_top_words):
for topic_idx, topic in enumerate(model.components_):
message = "Topic #%d: " % topic_idx
message += ", ".join([feature_names[i]
for i in topic.argsort()[:-n_top_words - 1:-1]])
print(message)
print()print_top_words(nmf, tfidf_vectorizer.get_feature_names(), n_top_words=3)
这是结果。看起来主题 0 是关于教授和课程的;题目 1 是关于作业,题目 3 是关于课本。请注意,我们不知道这里的最佳主题数量是多少。我们用 3 只是因为我们的样本量很小。在实践中,您可能需要进行网格搜索来找到最佳的主题数量。
LDA 模型
潜在狄利克雷分配是用于诸如文本语料库的离散数据集集合的生成概率模型。它也是一个主题模型,用于从文档集合中发现抽象主题。
在我们的例子中,我们使用函数***LatentDirichletAllocation***
,它“实现了在线变分贝叶斯算法,并支持在线和批量更新方法”。这里我们展示了一个学习方法被设置为默认值“在线”的例子。
Source: [https://scikit-learn.org/stable/auto_examples/applications/plot_topics_extraction_with_nmf_lda.html](https://scikit-learn.org/stable/auto_examples/applications/plot_topics_extraction_with_nmf_lda.html)from sklearn.decomposition import LatentDirichletAllocation
tfidf_vectorizer = TfidfVectorizer(stop_words=stoplist, ngram_range=(2,3))
lda = LatentDirichletAllocation(n_components=3)
pipe = make_pipeline(tfidf_vectorizer, lda)
pipe.fit(df['reviews'])def print_top_words(model, feature_names, n_top_words):
for topic_idx, topic in enumerate(model.components_):
message = "Topic #%d: " % topic_idx
message += ", ".join([feature_names[i]
for i in topic.argsort()[:-n_top_words - 1:-1]])
print(message)
print()print_top_words(lda, tfidf_vectorizer.get_feature_names(), n_top_words=3)
现在你知道如何用 Python 做一些基本的文本分析了。出于演示目的,我们的示例具有非常有限的数据大小。现实世界中的文本分析将更具挑战性和趣味性。希望你喜欢这篇文章。谢谢!
参考
https://stack overflow . com/questions/11763613/python-list-of-ngrams-with-frequency/11834518
使用自然语言处理的文本分析和特征工程
语言检测、文本清理、长度测量、情感分析、命名实体识别、N 元语法频率、词向量、主题建模
摘要
在本文中,我将使用 NLP 和 Python 来解释如何为您的机器学习模型分析文本数据和提取特征。
【NLP(自然语言处理) 是人工智能的一个领域,研究计算机和人类语言之间的交互,特别是如何给计算机编程,以处理和分析大量的自然语言数据。NLP 通常用于文本数据的分类。文本分类就是根据文本数据的内容给文本数据分配类别的问题。文本分类最重要的部分是特征工程:从原始文本数据中为机器学习模型创建特征的过程。
在本文中,我将解释不同的方法来分析文本并提取可用于构建分类模型的特征。我将展示一些有用的 Python 代码,这些代码可以很容易地应用于其他类似的情况(只需复制、粘贴、运行),并通过注释遍历每一行代码,以便您可以复制这个示例(下面是完整代码的链接)。
[## mdipietro 09/data science _ 人工智能 _ 实用工具
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
我将使用“新闻类别数据集”(下面的链接),其中为您提供了从赫芬顿邮报获得的 2012 年至 2018 年的新闻标题,并要求您将它们归类到正确的类别中。
根据标题和简短描述识别新闻的类型
www.kaggle.com](https://www.kaggle.com/rmisra/news-category-dataset)
特别是,我将经历:
- 环境设置:导入包并读取数据。
- 语言检测:了解哪些自然语言数据在。
- 文本预处理:文本清洗和转换。
- 长度分析:用不同的度量标准测量。
- 情感分析:确定文本是正面的还是负面的。
- 命名实体识别:用预定义的类别(如人名、组织、位置)标记文本。
- 词频:找到最重要的n-克。
- 单词向量:将单词转换成数字。
- 主题建模:从语料库中提取主要主题。
设置
首先,我需要导入以下库。
**## for data**
import **pandas** as pd
import **collections** import **json****## for plotting**
import **matplotlib**.pyplot as plt
import **seaborn** as sns
import **wordcloud****## for text processing** import **re**
import **nltk****## for language detection**
import **langdetect** **## for sentiment**
from **textblob** import TextBlob**## for ner**
import **spacy****## for vectorizer** from **sklearn** import feature_extraction, manifold**## for word embedding** import **gensim**.downloader as gensim_api**## for topic modeling**
import **gensim**
数据集包含在一个 json 文件中,所以我首先用 json 包将它读入一个字典列表,然后将其转换成一个 pandas Dataframe。
lst_dics = []
with **open**('data.json', mode='r', errors='ignore') as json_file:
for dic in json_file:
lst_dics.append( json.loads(dic) )**## print the first one**
lst_dics[0]
原始数据集包含超过 30 个类别,但是出于本教程的目的,我将使用 3 个类别的子集:娱乐、政治和技术。
**## create dtf**
dtf = pd.DataFrame(lst_dics)**## filter categories**
dtf = dtf[ dtf["category"].isin(['**ENTERTAINMENT**','**POLITICS**','**TECH**']) ][["category","headline"]]**## rename columns**
dtf = dtf.rename(columns={"category":"**y**", "headline":"**text**"})**## print 5 random rows**
dtf.sample(5)
为了理解数据集的组成,我将通过用条形图显示标签频率来研究单变量分布(只有一个变量的概率分布)。
**x = "y"**fig, ax = plt.subplots()
fig.suptitle(x, fontsize=12)
dtf[x].reset_index().groupby(x).count().sort_values(by=
"index").plot(kind="barh", legend=False,
ax=ax).grid(axis='x')
plt.show()
数据集是不平衡的:与其他的相比,科技新闻的比例真的很小。这可能是建模过程中的一个问题,数据集的重新采样可能会有所帮助。
现在一切都设置好了,我将从清理数据开始,然后我将从原始文本中提取不同的见解,并将它们作为 dataframe 的新列添加。这个新信息可以用作分类模型的潜在特征。
我们开始吧,好吗?
语言检测
首先,我想确保我处理的是同一种语言,并且使用了 langdetect 包,这真的很简单。为了举例说明,我将在数据集的第一个新闻标题上使用它:
txt = dtf["text"].iloc[0]print(txt, " --> ", **langdetect**.detect(txt))
让我们为整个数据集添加一个包含语言信息的列:
dtf['**lang**'] = dtf[**"text"**].apply(lambda x: **langdetect**.detect(x) if
x.strip() != "" else "")dtf.head()
dataframe 现在有一个新列。使用之前的相同代码,我可以看到有多少种不同的语言:
即使有不同的语言,也是以英语为主。因此,我将过滤英语新闻。
dtf = dtf[dtf["**lang**"]=="**en**"]
文本预处理
数据预处理是准备原始数据以使其适合机器学习模型的阶段。对于自然语言处理,这包括文本清理,停用词删除,词干和词汇化。
文本清理步骤根据数据类型和所需任务而有所不同。通常,在文本被标记化之前,字符串被转换成小写,标点符号被删除。记号化是将一个字符串拆分成一系列字符串(或“记号”)的过程。
让我们再次以第一个新闻标题为例:
**print("--- original ---")**
print(txt)**print("--- cleaning ---")**
txt = re.sub(r'[^\w\s]', '', str(txt).lower().strip())
print(txt)**print("--- tokenization ---")**
txt = txt.split()
print(txt)
我们要保留列表中的所有令牌吗?我们没有。事实上,我们希望删除所有不能提供额外信息的单词。在这个例子中,最重要的词是“ song ”,因为它可以将任何分类模型指向正确的方向。相比之下,像“和”、“代表”、“代表”这样的词就没什么用了,因为它们可能出现在数据集中的几乎每个观察值中。这些是停用词的例子。这种表达通常指的是一种语言中最常见的单词,但没有一个通用的停用词列表。
我们可以用NLTK(自然语言工具包)为英语词汇创建一个通用停用词列表,NLTK 是一套用于符号和统计自然语言处理的库和程序。
lst_stopwords = **nltk**.corpus.stopwords.words("**english**")
lst_stopwords
让我们从第一个新闻标题中去掉这些停用词:
**print("--- remove stopwords ---")**
txt = [word for word in txt if word not in lst_stopwords]
print(txt)
我们需要非常小心停用词,因为如果您删除了错误的令牌,您可能会丢失重要信息。例如,单词“ will ”被删除,我们丢失了这个人是威尔·史密斯的信息。考虑到这一点,在删除停用词之前对原始文本进行一些手动修改会很有用(例如,用“威尔·史密斯”替换“威尔·史密斯”)。
现在我们有了所有有用的标记,我们可以应用单词转换了。词干和词条化都生成单词的词根形式。区别在于词干可能不是一个实际的单词,而 lemma 是一个实际的语言单词(而且词干通常更快)。那些算法都是由 NLTK 提供的。
继续这个例子:
**print("--- stemming ---")**
ps = **nltk**.stem.porter.**PorterStemmer**()
print([ps.stem(word) for word in txt])**print("--- lemmatisation ---")**
lem = **nltk**.stem.wordnet.**WordNetLemmatizer**()
print([lem.lemmatize(word) for word in txt])
正如你所看到的,一些单词发生了变化:“joins”变成了它的词根形式“join”,就像“cups”一样。另一方面,“官方”只是在词干上有所变化,变成了词干“offici”,它不是一个单词,是通过去掉后缀“-al”创建的。
我将把所有这些预处理步骤放在一个函数中,并将其应用于整个数据集。
**'''
Preprocess a string.
:parameter
:param text: string - name of column containing text
:param lst_stopwords: list - list of stopwords to remove
:param flg_stemm: bool - whether stemming is to be applied
:param flg_lemm: bool - whether lemmitisation is to be applied
:return
cleaned text
'''**
def **utils_preprocess_text**(text, flg_stemm=False, flg_lemm=True, lst_stopwords=None):
**## clean (convert to lowercase and remove punctuations and characters and then strip)**
text = re.sub(r'[^\w\s]', '', str(text).lower().strip())
**## Tokenize (convert from string to list)**
lst_text = text.split() **## remove Stopwords**
if lst_stopwords is not None:
lst_text = [word for word in lst_text if word not in
lst_stopwords]
**## Stemming (remove -ing, -ly, ...)**
if flg_stemm == True:
ps = nltk.stem.porter.PorterStemmer()
lst_text = [ps.stem(word) for word in lst_text]
**## Lemmatisation (convert the word into root word)**
if flg_lemm == True:
lem = nltk.stem.wordnet.WordNetLemmatizer()
lst_text = [lem.lemmatize(word) for word in lst_text]
**## back to string from list**
text = " ".join(lst_text)
return text
请注意,您不应该同时应用词干化和词汇化。这里我将使用后者。
dtf["text_clean"] = dtf["text"].apply(lambda x: **utils_preprocess_text**(x, flg_stemm=False, **flg_lemm=True**, lst_stopwords))
和以前一样,我创建了一个新的专栏:
dtf.head()
print(dtf["**text**"].iloc[0], " --> ", dtf["**text_clean**"].iloc[0])
长度分析
看一看文本的长度是很重要的,因为这是一个简单的计算,可以给出很多见解。例如,也许我们足够幸运地发现一个类别比另一个类别系统地更长,并且长度仅仅是构建模型所需的唯一特征。不幸的是,情况并非如此,因为新闻标题有相似的长度,但值得一试。
文本数据有几种长度度量。我将举一些例子:
- 字数统计:统计文本中的记号数(用空格隔开)
- 字符数:合计每个令牌的字符数
- 句子计数:计算句子的数量(用句号分隔)
- 平均字数:字数除以字数(字数/字数)
- 平均句子长度:句子长度之和除以句子数量(字数/句子数)
dtf['word_count'] = dtf["text"].apply(lambda x: len(str(x).split(" ")))dtf['char_count'] = dtf["text"].apply(lambda x: sum(len(word) for word in str(x).split(" ")))dtf['sentence_count'] = dtf["text"].apply(lambda x: len(str(x).split(".")))dtf['avg_word_length'] = dtf['char_count'] / dtf['word_count']dtf['avg_sentence_lenght'] = dtf['word_count'] / dtf['sentence_count']dtf.head()
让我们看看我们通常的例子:
这些新变量相对于目标的分布是怎样的?为了回答这个问题,我将看看二元分布(两个变量如何一起移动)。首先,我将把整个观察集分成 3 个样本(政治、娱乐、科技),然后比较样本的直方图和密度。如果分布不同,那么变量是可预测的,因为 3 组具有不同的模式。
例如,让我们看看字符数是否与目标变量相关:
**x, y = "char_count", "y"**fig, ax = plt.subplots(nrows=1, ncols=2)
fig.suptitle(x, fontsize=12)
for i in dtf[y].unique():
sns.distplot(dtf[dtf[y]==i][x], hist=True, kde=False,
bins=10, hist_kws={"alpha":0.8},
axlabel="histogram", ax=ax[0])
sns.distplot(dtf[dtf[y]==i][x], hist=False, kde=True,
kde_kws={"shade":True}, axlabel="density",
ax=ax[1])
ax[0].grid(True)
ax[0].legend(dtf[y].unique())
ax[1].grid(True)
plt.show()
这三个类别具有相似的长度分布。这里,密度图非常有用,因为样本大小不同。
情感分析
情感分析是将文本数据的主观情感通过数字或类别表示出来。计算情感是自然语言处理中最困难的任务之一,因为自然语言充满了歧义。例如,短语“这是如此糟糕,以至于它是好的”有不止一种解释。一个模型可以给单词“好的”分配一个积极的信号,给单词“坏的”分配一个消极的信号,产生一个中性的情绪。发生这种情况是因为背景未知。
最好的方法是训练你自己的情绪模型,使之与你的数据完全吻合。当没有足够的时间或数据时,可以使用预先训练的模型,如 Textblob 和 Vader 。text blob,建立在 NLTK 之上,是最受欢迎的一个,它可以给单词分配极性,并把整篇文本的情感估计为平均值。另一方面, 【维达】 (价觉词典和情感推理机)是一个基于规则的模型,在社交媒体数据上工作得特别好。
我将使用 Textblob 添加一个情感特征:
dtf["sentiment"] = dtf[column].apply(lambda x:
**TextBlob**(x).sentiment.polarity)
dtf.head()
print(dtf["text"].iloc[0], " --> ", dtf["sentiment"].iloc[0])
品类和情绪之间有模式吗?
大多数标题都有一种中性的情绪,除了政治新闻倾向于负面,科技新闻倾向于正面。
命名实体识别
NER ( 命名实体识别)是用预定义的类别(如人名、组织、位置、时间表达式、数量等)标记非结构化文本中提到的命名实体的过程。
训练 NER 模型非常耗时,因为它需要非常丰富的数据集。幸运的是,有人已经为我们做了这项工作。最好的开源 NER 工具之一是 SpaCy 。它提供了不同的 NLP 模型,能够识别几类实体。
来源:空间
我将在我们通常的标题(原始文本,未经预处理)上使用SpaCymodelen _ core _ web _ LG(基于 web 数据训练的英语大模型)来举例说明:
**## call model**
ner = **spacy**.load("**en_core_web_lg**")**## tag text**
txt = dtf["text"].iloc[0]
doc = **ner**(txt)**## display result**
spacy.**displacy**.render(doc, style="ent")
这很酷,但是我们如何把它变成一个有用的特性呢?这是我要做的:
- 对数据集中的每个文本观察值运行 NER 模型,就像我在前面的例子中所做的那样。
- 对于每个新闻标题,我将把所有已识别的实体放入一个新的列(名为“tags”)中,同时列出该实体在文本中出现的次数。在本例中,应该是
{('威尔·史密斯','人'):1,
('迪普','人'):1,
('尼基·贾姆','人'):1,
(“2018 世界杯的','事件'):1 }
- 然后,我将为每个标签类别(Person、Org、Event 等)创建一个新列,并计算每个类别中找到的实体的数量。在上面的例子中,这些特性是
tags_PERSON = 3
tags_EVENT = 1
**## tag text and exctract tags into a list**
dtf["tags"] = dtf["text"].apply(lambda x: [(tag.text, tag.label_)
for tag in ner(x).ents] )**## utils function to count the element of a list** def **utils_lst_count**(lst):
dic_counter = collections.Counter()
for x in lst:
dic_counter[x] += 1
dic_counter = collections.OrderedDict(
sorted(dic_counter.items(),
key=lambda x: x[1], reverse=True))
lst_count = [ {key:value} for key,value in dic_counter.items() ]
return lst_count **## count tags**
dtf["tags"] = dtf["tags"].apply(lambda x: **utils_lst_count**(x)) **## utils function create new column for each tag category** def **utils_ner_features**(lst_dics_tuples, tag):
if len(lst_dics_tuples) > 0:
tag_type = []
for dic_tuples in lst_dics_tuples:
for tuple in dic_tuples:
type, n = tuple[1], dic_tuples[tuple]
tag_type = tag_type + [type]*n
dic_counter = collections.Counter()
for x in tag_type:
dic_counter[x] += 1
return dic_counter[tag]
else:
return 0 **## extract features**
tags_set = []
for lst in dtf["tags"].tolist():
for dic in lst:
for k in dic.keys():
tags_set.append(k[1])
tags_set = list(set(tags_set))
for feature in tags_set:
dtf["tags_"+feature] = dtf["tags"].apply(lambda x:
**utils_ner_features**(x, feature)) **## print result**
dtf.head()
现在我们可以对标签类型分布有一个宏观的看法。让我们以 ORG 标签(公司和组织)为例:
为了更深入地分析,我们需要解包我们在前面的代码中创建的列“tags”。让我们为其中一个标题类别画出最常见的标签:
y = "**ENTERTAINMENT**"
tags_list = dtf[dtf["y"]==y]["tags"].sum()
map_lst = list(map(lambda x: list(x.keys())[0], tags_list))
dtf_tags = pd.DataFrame(map_lst, columns=['tag','type'])
dtf_tags["count"] = 1
dtf_tags = dtf_tags.groupby(['type',
'tag']).count().reset_index().sort_values("count",
ascending=False)
fig, ax = plt.subplots()
fig.suptitle("Top frequent tags", fontsize=12)
sns.barplot(x="count", y="tag", hue="type",
data=dtf_tags.iloc[:top,:], dodge=False, ax=ax)
ax.grid(axis="x")
plt.show()
继续 NER 的另一个有用的应用:你还记得当我们从""的名字中去掉单词""的时候吗?这个问题的一个有趣的解决方案是用“ Will_Smith ”替换“威尔·史密斯”,这样它就不会受到停用词删除的影响。因为遍历数据集中的所有文本来更改名称是不可能的,所以让我们使用 SpaCy 来实现。我们知道, SpaCy 可以识别人名,因此我们可以用它来进行姓名检测然后修改字符串。**
****## predict wit NER** txt = dtf["text"].iloc[0]
entities = ner(txt).ents**## tag text**
tagged_txt = txt
for tag in entities:
tagged_txt = re.sub(tag.text, "_".join(tag.text.split()),
tagged_txt) **## show result**
print(tagged_txt)**
字频率
到目前为止,我们已经看到了如何通过分析和处理整个文本来进行特征工程。现在我们将通过计算单词的出现频率来了解单词的重要性。一个 n -gram 是来自给定文本样本的 n 项的连续序列。当 n -gram 的大小为 1 时,称为一元 gram(大小为 2 的是二元 gram)。
例如,短语“我喜欢这篇文章”可以分解为:
- 4 unigrams:“I”、 like 、 this 、 article ”
- 3 个大人物:“我喜欢”、“喜欢这个”、“这篇文章”
我将以政治新闻为例,展示如何计算单词和双词的频率。
**y = "**POLITICS**"
corpus = dtf[dtf["y"]==y]["text_clean"]lst_tokens = **nltk**.tokenize.word_tokenize(corpus.str.cat(sep=" "))
fig, ax = plt.subplots(nrows=1, ncols=2)
fig.suptitle("Most frequent words", fontsize=15)
**## unigrams**
dic_words_freq = nltk.FreqDist(lst_tokens)
dtf_uni = pd.DataFrame(dic_words_freq.most_common(),
columns=["Word","Freq"])
dtf_uni.set_index("Word").iloc[:top,:].sort_values(by="Freq").plot(
kind="barh", title="Unigrams", ax=ax[0],
legend=False).grid(axis='x')
ax[0].set(ylabel=None)
**## bigrams**
dic_words_freq = nltk.FreqDist(nltk.ngrams(lst_tokens, 2))
dtf_bi = pd.DataFrame(dic_words_freq.most_common(),
columns=["Word","Freq"])
dtf_bi["Word"] = dtf_bi["Word"].apply(lambda x: " ".join(
string for string in x) )
dtf_bi.set_index("Word").iloc[:top,:].sort_values(by="Freq").plot(
kind="barh", title="Bigrams", ax=ax[1],
legend=False).grid(axis='x')
ax[1].set(ylabel=None)
plt.show()**
如果有只出现在一个类别中的 n -grams(例如政治新闻中的“共和党”),这些可以成为新的特征。一种更费力的方法是将整个语料库矢量化,并将所有单词用作特征(单词袋方法)。
现在,我将向您展示如何在您的数据框架中添加词频作为一项功能。我们只需要来自 Scikit-learn 的计数矢量器,Python 中最流行的机器学习库之一。矢量器将文本文档的集合转换成令牌计数的矩阵。我用 3 个 n-grams 举个例子:“票房(娱乐圈频繁)、共和党(政界频繁)、“苹果”(科技界频繁)。
*lst_words = ["**box office**", "**republican**", "**apple**"]***## count*** lst_grams = [len(word.split(" ")) for word in lst_words]
vectorizer = feature_extraction.text.**CountVectorizer**(
vocabulary=lst_words,
ngram_range=(min(lst_grams),max(lst_grams)))dtf_X = pd.DataFrame(vectorizer.fit_transform(dtf["text_clean"]).todense(), columns=lst_words)***## add the new features as columns*** dtf = pd.concat([dtf, dtf_X.set_index(dtf.index)], axis=1)
dtf.head()*
可视化相同信息的一个好方法是使用单词云,其中每个标签的频率用字体大小和颜色显示。
*wc = **wordcloud**.WordCloud(background_color='black', max_words=100,
max_font_size=35)
wc = wc.generate(str(corpus))
fig = plt.figure(num=1)
plt.axis('off')
plt.imshow(wc, cmap=None)
plt.show()*
词向量
最近,NLP 领域开发了新的语言模型,该模型依赖于神经网络架构,而不是更传统的 n-gram 模型。这些新技术是一套语言建模和特征学习技术,其中单词被转换成实数向量,因此它们被称为单词嵌入。
单词嵌入模型通过构建在所选单词之前和之后会出现什么标记的概率分布,将特定单词映射到向量。这些模型很快变得流行起来,因为一旦有了实数而不是字符串,就可以进行计算了。例如,为了找到相同上下文的单词,可以简单地计算向量距离。
有几个 Python 库可以处理这种模型。 SpaCy 是一个,不过既然我们已经用过了,我就说说另一个著名的包: Gensim 。一个使用现代统计机器学习的无监督主题建模和自然语言处理的开源库。使用 Gensim ,我将加载一个预先训练好的手套模型。 GloVe (全局向量)是一种无监督学习算法,用于获得大小为 300 的单词的向量表示。
*nlp = **gensim_api**.load("**glove-wiki-gigaword-300**")*
我们可以使用这个对象将单词映射到向量:
***word = "love"**nlp[word]*
*nlp[word].shape*
现在让我们看看什么是最接近的词向量,或者换句话说,最常出现在相似上下文中的词。为了在二维空间中绘制向量,我需要将维度从 300 减少到 2。我将用来自 Scikit-learn 的t-分布式随机邻居嵌入来做这件事。* t-SNE 是一种可视化高维数据的工具,它将数据点之间的相似性转换为联合概率。*
***## find closest vectors**
labels, X, x, y = [], [], [], []
for t in nlp.**most_similar**(word, topn=20):
X.append(nlp[t[0]])
labels.append(t[0])**## reduce dimensions**
pca = manifold.**TSNE**(perplexity=40, n_components=2, init='pca')
new_values = pca.fit_transform(X)
for value in new_values:
x.append(value[0])
y.append(value[1])**## plot**
fig = plt.figure()
for i in range(len(x)):
plt.scatter(x[i], y[i], c="black")
plt.annotate(labels[i], xy=(x[i],y[i]), xytext=(5,2),
textcoords='offset points', ha='right', va='bottom')**## add center**
plt.scatter(x=0, y=0, c="red")
plt.annotate(word, xy=(0,0), xytext=(5,2), textcoords='offset
points', ha='right', va='bottom')*
主题建模
Genism 包专门用于主题建模。主题模型是一种用于发现出现在文档集合中的抽象“主题”的统计模型。
我将展示如何使用 LDA (潜在的狄利克雷分配)提取主题:一种生成统计模型,它允许通过未观察到的组来解释观察集,从而解释为什么数据的某些部分是相似的。基本上,文档被表示为潜在主题的随机混合,其中每个主题由单词的分布来表征。
让我们看看我们能从科技新闻中提取出什么话题。我需要指定模型必须聚类的主题数量,我将尝试使用 3:
*y = "**TECH**"
corpus = dtf[dtf["y"]==y]["text_clean"] **## pre-process corpus**
lst_corpus = []
for string in corpus:
lst_words = string.split()
lst_grams = [" ".join(lst_words[i:i + 2]) for i in range(0,
len(lst_words), 2)]
lst_corpus.append(lst_grams)**## map words to an id**
id2word = gensim.corpora.Dictionary(lst_corpus)**## create dictionary word:freq**
dic_corpus = [id2word.doc2bow(word) for word in lst_corpus] **## train LDA**
lda_model = gensim.models.ldamodel.**LdaModel**(corpus=dic_corpus, id2word=id2word, **num_topics=3**, random_state=123, update_every=1, chunksize=100, passes=10, alpha='auto', per_word_topics=True)
**## output**
lst_dics = []
for i in range(0,**3**):
lst_tuples = lda_model.get_topic_terms(i)
for tupla in lst_tuples:
lst_dics.append({"topic":i, "id":tupla[0],
"word":id2word[tupla[0]],
"weight":tupla[1]})
dtf_topics = pd.DataFrame(lst_dics,
columns=['topic','id','word','weight'])
**## plot**
fig, ax = plt.subplots()
sns.barplot(y="word", x="weight", hue="topic", data=dtf_topics, dodge=False, ax=ax).set_title('Main Topics')
ax.set(ylabel="", xlabel="Word Importance")
plt.show()*
试图只在 3 个主题中捕捉 6 年的内容可能有点困难,但正如我们所见,关于苹果公司的一切都以同一个主题结束。
结论
这篇文章是一个教程,演示了如何用 NLP 分析文本数据,并为机器学习模型提取特征。
我展示了如何检测数据使用的语言,以及如何预处理和清理文本。然后我解释了不同的长度度量,用 Textblob 做了情感分析,我们使用 SpaCy 进行命名实体识别。最后,我解释了使用 Scikit-learn 的传统词频方法和使用 Gensim 的现代语言模型之间的区别。现在,您已经基本了解了 NLP 的所有基础知识,可以开始处理文本数据了。
我希望你喜欢它!如有问题和反馈,或者只是分享您感兴趣的项目,请随时联系我。
👉我们来连线👈
本文是系列文章 NLP 与 Python 的一部分,参见:
* [## 使用 NLP 的文本摘要:TextRank vs Seq2Seq vs BART
使用 Python、Gensim、Tensorflow、Transformers 进行自然语言处理
towardsdatascience.com](/text-summarization-with-nlp-textrank-vs-seq2seq-vs-bart-474943efeb09) [## 基于自然语言处理的文本分类:Tf-Idf vs Word2Vec vs BERT
预处理、模型设计、评估、词袋的可解释性、词嵌入、语言模型
towardsdatascience.com](/text-classification-with-nlp-tf-idf-vs-word2vec-vs-bert-41ff868d1794) [## 用于无模型训练的文本分类的 BERT
如果没有带标签的训练集,请使用 BERT、单词嵌入和向量相似度
towardsdatascience.com](/text-classification-with-no-model-training-935fe0e42180) [## 带 NLP 的 AI 聊天机器人:语音识别+变形金刚
用 Python 构建一个会说话的聊天机器人,与你的人工智能进行对话
towardsdatascience.com](/ai-chatbot-with-nlp-speech-recognition-transformers-583716a299e9)*
R 中的文本分析
马克·吐温著作中的文本标记介绍。
帕特里克·托马索在 Unsplash 上的照片
"经典——一本人们称赞却不去读的书."
[人名]马克·吐温(美国幽默大师、小说家、作家、演说家)
希望你已经证明了马克·吐温是错的,并且已经沉迷于他的一部经典作品。如果不是,也许这是一个通过文本分析了解他的书的很好的入门。
什么是文本分析?
文本分析是检查文本形式的非结构化数据的过程,以收集关于感兴趣的模式和主题的一些见解。
为什么重要?
文本分析之所以重要有很多原因,其中一个主要原因是理解我们每天使用的应用程序和服务中使用的情绪和情感。使用文本分析,我们可以从推文、电子邮件、短信、广告、地图等等中提取有意义的信息。
如果你想更深入地研究这个主题,这里有一本很好的书——《R 中的文本挖掘》
在本教程中,我将向您展示如何开始使用 r 中必要的文本分析功能。
R 是一种用于统计计算和图形的语言和环境。
入门指南
我们将不得不安装 gutenbergr 库,这将使我们能够访问他们的可用书籍/出版物库。
gutenbergr 包帮助你从 项目 Gutenberg 集合中下载并处理公有领域作品。这包括用于下载书籍的工具(和剥离页眉/页脚信息),以及可用于查找感兴趣的单词的完整的古腾堡项目元数据数据集。
让我们在 R Studio 中安装并加载这个库
install.packages("gutenbergr")library(gutenbergr)
马克·吐温的书
现在,我们将从古腾堡图书馆调出几本马克·吐温写的书。
在古腾堡图书馆,每本书都有一个 ID 号,我们需要用它来识别它们的位置。
mark_twain <- gutenberg_download(c(**76**, **74**, **3176**, **245**))
我们使用 gutenberg_download 函数提取图书,并将其保存到 mark_twain 数据框中。
mark_twin 数据帧的快照
通过使用 R,书籍被放入特定的行,并被标记到与书籍 ID 号相对应的列。很明显,这些数据是杂乱的,目前对分析没有太大用处。
识别停用词
当您分析任何文本时,总是会有冗余的单词,这些单词会根据您试图识别的模式或趋势而扭曲结果。这些被称为停止字。
您可以决定是否要删除停用字词,但在本例中,我们将继续删除它们。
首先,我们需要加载库 tidytext。
library(tidytext)
接下来,我们将查看整个 R 数据库中的 stop_words(不在马克·吐温的书中)
data(stop_words)
stop_words 函数的前几行视图
注意我们有 1,139 行。
标记和删除停用词
现在,我们将使用 dplyr 库中的管道方法来删除停用词并标记我们的文本。
标记化的任务是把它分割成小块
把标记化想象成逐字分解一个句子。这使得文本分析软件能够为分析提供更多的结构。
来源:斯坦福大学自然语言处理——标记化示例
我们将通过管道将几个步骤连接在一起,在对 mark_twain 数据帧进行令牌化的同时移除停止字。
library(dplyr)tidy_mark_twain <- mark_twain %>%
unnest_tokens(word, text) %>% #**tokenize**
anti_join(stop_words) #**remove stop words**print(tidy_mark_twain)
步骤:
- 使用 u nnest_tokens() 公式并在输入中传递来指定我们想要标记的内容以及从哪里访问文本。
- 使用 anti_join() ,我们基本上排除了在停用字词数据库中找到的所有字词。
- 我们将所有的工作保存在一个新的变量中。
- 如果你看看
我们可以看到单词没有被单独分开,并且图书 ID 被标记在上面
您是否注意到,由于每个对象都被标记化了,我们现在有 182,706 行!与相比,我们更早得到了 1139 行。这是因为每个单词都有自己的一行。
词的频率分布
我们的目标是找到数据中的模式,所以一个很好的方法来查看哪些词是排序和找到最常用的词。
tidy_mark_twain %>%
count(word, sort=TRUE)
最流行的词是时间。
如果你读过马克·吐温的任何一本书,毫无疑问汤姆是第二个最受欢迎的单词,但有趣的是看到单词是最频繁使用的单词——1226!
可视化数据
使用 ggplot2 库,我们可以添加一些可视上下文来查看哪些单词是最常用的。
*library(ggplot2)freq_hist <-tidy_mark_twain %>%
count(word, sort=TRUE) %>%
filter(n > 400) %>%
mutate(word = reorder(word,n )) %>%
ggplot(aes(word, n))+
geom_col(fill= 'lightgreen')+
xlab(NULL)+
coord_flip()print(freq_hist)*
完成了一些关键步骤来提供清晰的图表:
- filter() —确保我们不会查看所有单词的总字数,因为那样会太多。这里我们设置了一个超过 400 个计数的单词边界。
- mutate() —提供更好的单词组织顺序。
- coord_flip() —旋转图形,使其看起来更美观。
如果剩下的代码面生,可以查看我的 ggplot 教程 了解更多。
概观
文本无处不在,这意味着有无限的机会来分析和理解非结构化数据。现在你有了一个基础,应该可以帮助你开始你的文本分析之旅。
请继续关注——我将分享更多关于使用 Twitter 的 API 提取和抓取推文的教程。
文本分析:关于你的个性,你的 LinkedIn 个人资料摘要说明了什么?
使用自然语言处理技术来预测你的性格。
我的团队使用一个人写的文本来尝试解开他们的个性。这对人力资源行业来说意义重大,他们可以取消在线测试,使用更非正式的方法来确定合适的候选人。我们的想法是分解项目,交流我们的思考过程和背后的动机。
Shamak Tiwatane 拍摄的照片(个人收藏)
想想你上一次申请工作的时候。如果你有一份还算过得去的简历,并且符合公司对该职位的最低标准,你就会收到一封来自人力资源部门的标准化电子邮件,要求你完成在线评估。如果这个角色是技术性的,你会有一些与你所受教育相关的具体问题。然而,在大多数情况下,这是公司用来评估你的性格类型的正式测试。此信息用于确定候选人是否适合该职位和组织。
我记得在我本科学位的最后一年,我几乎申请了任何一家提供研究生课程的公司。我通常填写所需的申请,回答那些一般性的问题,并附上我的简历。然后就在第二天,有两件事情发生了。要么我会收到一封断然拒绝的电子邮件,公司会感谢我的兴趣,并告诉我经过仔细考虑后,他们无法继续我的申请。或者另一个结果是他们很高兴邀请我完成一个交互式的研究生评估。就互动而言,他们通常指的是带有复选框的在线表格,如果你强烈同意或不同意某个标准陈述,你可以在其中打勾。
四大事务所之一邀请我进行类似的评估。关于心理测试的陈述是“我发现很难接近他人”,我应该勾选:
- S 强烈同意
- 同意
- 既不同意也不反对
- 不同意
- 强烈反对
事实上,我确实发现很难接近别人。因此,理想情况下,我应该选择‘同意’这种说法,但当你正在完成对世界上最大的专业服务公司之一的评估时,你会选择‘强烈不同意’,甚至连眼睛都不眨一下!
准确的性格评估是很难的,在某种程度上可能会被候选人篡改,就像我一样。鉴于就业往往与巨大的经济和社会利益相关,求职者受到激励,以最能反映“理想”候选人特征的方式描绘自己。这可能导致候选人掩盖他们的真实个性,导致不准确的测试评估,特别是在正式环境下进行的测试。相比之下,在轻松或非正式的环境中对个性的评估可能会导致对个人个性的更具代表性的估计。
为此,我们创建了一个人格特质估计器,它以非正式的书面文本作为输入,并基于 16 个迈尔斯-布里格斯人格类型指标维度给出人格估计。基本上是根据你写的东西来预测你的性格。
迈尔斯-布里格斯人格类型指标(MBTI)是一个人格类型系统,它从四个方面评估个人的性格特征:
图片: 斯凯古尔德商业内幕
最初的测试包括一份自我报告的问卷,旨在将受访者分为 16 种性格类型(上述维度的可能组合),通常用于衡量一个人对自我表达、信息处理、规划和决策的偏好。
我们已经将人格特质检测作为监督学习问题来处理。我们的过程是训练四个分类器来预测四种 MBTI 类型的二元结果。因此,从本质上来说,对于一段文字摘录,每个分类器都会预测 MBTI 人格类型的四个维度中的一个。
数据集和预处理
在我们的分类器开发中使用了两个训练语料库,而第三个数据集用于这项工作的公司概况映射部分。
- 训练集:由 8600 个带标签的条目和帖子组成的 Kaggle 数据集,以半随意的语气编写
- 训练集:由 9300 篇帖子组成的 Reddit 数据集,以非正式的语气编写
- 测试集:从 10 家公司的 12000 名员工中收集的 LinkedIn 数据集,以随意到专业的语气编写
考虑用于文本丰富的特征(特征工程)
接下来是标记化过程。按作者列出的流程
上面的过程总结了生成用于创建术语-文档矩阵的最终标记列表所采取的步骤。该矩阵包含在语料库中出现的单词的频率,它广泛用于文本分析领域,以执行诸如文档相似性或情感分析之类的任务。
虽然许多步骤遵循标准惯例,但源文本的性质意味着我们也删除了 URL 和电子邮件,以及对 MBTI 类型和 Reddit subreddit URLs 的引用。为了减少特征稀疏性,采用了一个引理满足过程来将单词简化为它们的基本形式,并且选择了引理满足而不是词干来尽可能多地保留基本单词中的含义。最初也考虑了标点和大写,但是,在初步调查后,我们无法在它们中找到任何有意义的预测能力,因此在标记化过程中也进行了标点删除和大小写降低。
我们遵循一种与文献中发现的方法一致的方法,并使用单词-单字将文本表示为加权的术语-文档矩阵。初步实验发现,(单词)二元语法和三元语法表示降低了预测性能,并在术语-文档矩阵中引入了稀疏性。类似地,字符 n-grams 产生不利的结果,因此不用于术语加权过程。最后,使用 tf-idf(术语频率-逆文档频率)加权过程将术语-文档矩阵转换为分类器使用的文本的归一化特征表示。我们使用正则表达式、SpaCy 和 scikit-learn 方法的组合来实现这些转换。初步实验确定,只需要前 2000 个最频繁出现的单字就可以获得足够的分类性能。除了 tf-idf 矩阵之外,还添加了其他几个功能来尝试捕捉文本中情感表达和个性的细微差别,如下所述。
语言特征 研究由 (Mairesse et al .,2006;梅尔等人,2006) 注意,外向性格的特点是更多地使用动词、副词和代词。外向类型的句子往往更简单,用词更少,词汇多样性更低,否定也更少。相反,内向的人一般会用更多的否定词,使用更广泛的词汇。为了识别这些差异,为每个用户帖子计算了一个形式分数(Heylighen 和 Dewaele,1999) :
其中频率是作为句子中所写单词总数的一部分来测量的词类。较高的正式程度表明文本中的表达语气较弱。计算每个用户帖子的形式分数,并记录为平均形式分数。空间位置(词类)标记用于标识等式中所需的词类。
此外,还计算了每个帖子的其他几个特征,以捕捉词汇多样性和单词用法:
- 类型与标记比率:唯一单词的数量除以单词总数。
- 每句话的平均字数:句子复杂性的一个代表
- 平均单词长度:词汇使用的代表
心理语言学特征 NRC(国家研究委员会)情感词典 (Mohammad and Turney,2013) 被用来识别沿着八个基本极点的单词:
- 欢乐-悲伤
- 愤怒-恐惧
- 信任-厌恶
- 期待-惊喜
该词典还用于确定大约 20,000 个单词的效价、唤醒和支配维度上的单词强度以及两个额外的情绪得分(积极和消极)。这样每个单词总共有 13 分。13 个 NRC 维度中的每一个都被转换为特征,方法是根据每个用户帖子中的频率对各个 NRC 单词值进行加权,并将每个特征表示为每个帖子中所写单词总数的一部分。这为每个帖子产生了 13 个 NRC 特征向量,这些向量被连接到 tf-idf 矩阵上用于分类器训练。
用于分类器训练的特征总结
分类器和参数说明
这项工作中的人格特质评估包括预测四个 MBTI 维度作为四个独立的二元结果。所以基本上给定一篇文章,我们会试着从中预测 4 种不同的结果,即
- 内向或外向,即
- 直观还是感应, N-S
- 思维或感觉, T-F
- 判断或感知, J-P
构建了五个分类器,用于确定四个人格维度中的每一个:
- 随机森林
- 支持向量机(SVM)
- 逻辑回归
- 伯努利朴素贝叶斯
- 多数类分类器
之所以选择随机森林分类器,是因为树的结构允许理解用于得出预测的决策逻辑。SVM 和 LR 分类器在文献中经常被报道为从文本进行个性预测的流行选择。使用(伯努利)Naïve 贝叶斯分类器作为对照来测试特征(或记号)之间的独立性假设。由于情感通常不仅来源于单词,还来源于单词与句子中其他单词的关系,因此人们会认为 Naïve 贝叶斯分类器在文本分类中表现不佳。最后,多数类分类器被用作基线分类器来与其他四个分类器进行性能比较。
分类器超参数规格
结果和讨论
在构建我们的个性分类器时,我们使用的最终特征是:单词单字、分享网址的比例、使用表情符号的比例;平均形式分数;类型与标记的比率;平均每句话字数;平均单词长度;以及 NRC 情绪、效价、唤醒、支配和情绪。我们认为这些特征中的每一个都在一定程度上反映了个人的个性模式,这些特征也在文学中使用。
样品内测试
四个 MBTI 维度上的分类器性能(所有特征)。按作者分类的图表
上图总结了使用 ROC 曲线的分类器性能,以及使用 30%保留进行测试的相关 AUC 分数。LR 分类器比 MBTI 分类器具有更高的精确度。除 SVM 分类器外,所有分类器在判断/感知和直觉/感觉维度上的准确率都较低,而思维/感觉维度的准确率最高。
所有分类器通常在这项工作的样本内测试部分表现良好,所达到的精度可与文献中从文本中发现的性格预测的类似工作相媲美(Gjurkovic 和 Šnajder,2018;Mairesse 等人,2007 年;Tchokni 等人,2014 年)。
重要词汇
随机森林分类器的前 20 个重要特征。按作者分类的图表
虽然与其他分类器相比,RF 分类器实现了较低的预测精度,但是可以对其进行检查以理解文本分类的决策过程。为了深入了解哪些词可能对某些 MBTI 类型有重要意义,上图显示了来自训练过的 RF 分类器的每个维度的 20 个最相关的特征。
I-E 维度中的数字-字母-数字记号( 7w8 、 7w6 、 8w7 )对应于记号化过程中未知的来自九型图系统的个性描述。I-E 维度也以使用互联网文化元素为标志,如表情符号( 😄 )和俚语( lol )。T- F 维度的特征对积极情感和情绪的词汇反应强烈(如感受、爱、美丽),而判断和感知维度似乎是由与合理化和分析相关的术语发出的信号(工作、时间、思考、需要、计划)。直觉和感知特征通常不对应任何具体的主题——而其他类型至少由一个强特征触发,N-S 维度没有这样的特征。这种缺乏区分可能是 RF 分类器准确预测 N-S 维度普遍困难的原因。
逻辑回归分类器的混淆矩阵。按作者分类的图表
LR 分类器的 16 路混淆矩阵如上图所示。分类器倾向于实现预测内向直觉类型的最大准确度,而预测外向感知类型的较低准确度。这可能反映了训练数据中的类别不平衡,因为在训练集中这些类型的组合相对较少。鉴于这些类型在日常生活中很少见,并且 N-S 和 J-P 维度通常更难通过文本来表达,分类器倾向于更具体的类型是意料之中的。
尽管如此,请注意,正确预测的密度往往是围绕具有相似维度的相似人格类型进行分组的,例如 INTP 和 INTJ。这反映了观察到的这些类型之间的内在相似性,人类可能也很难区分。因此,分类器已经设法识别这种细微差别。
同行测试
在前面的章节中,我们描述了几种个性分类器的发展以及它们在样本内测试中的准确性。在本节中,我们通过分析最佳分类器(LR)在对等体上的样本外性能来记录我们对它的分析。
使用 LR 分类器开发了一个两部分的问卷来识别回答者的个性。首先要求用户完成 Myers-Briggs 问卷,然后对以下问题提供书面回答:
- 说说你自己吧?
- 你擅长什么?
- 你喜欢你工作的什么?
然后,这些回答被用来通过分类器评估他们的个性。
对样本响应的评估 以下摘录由一位受访者撰写:
“一个充满激情和热情的人,他相信生活应该是充实的。绝对热爱表演艺术和时尚。我擅长与周围的人产生共鸣,并且非常坚持不懈地实现我的目标。我喜欢能够影响各种各样的人做出直接影响他们生活的决定。能够在公司愿景和公司最大的资产——员工及其需求——之间找到平衡点。”
他们的 MBTI 人格类型(由调查问卷确定)为 ENF J.
LR 分类器对被调查者 1 的人格类型预测。按作者分类的图表
而分类器预测这个人的性格类型为 ENF P (上图),因此成功地将 MBTI 四个维度中的三个分类。错误分类维度(J-P)估计应答者为 48% J 和 52% P,表明分类器对 J-P 维度的低置信度。我们认为,分类器已经成功识别了样本反应的表达语气,预测了 75%的外向性和 64%的情感。
结果的接近强化了这样一个观念,即人格是不稳定的,取决于许多因素。从这个意义上来说,用可能性的尺度来描述个性可能比用绝对数来描述更合适。来自这种表示的附加细节可能对招聘人员有帮助。
同伴测试实验的整体准确性 作为实验的一部分,我们能够收集 15 个不同个体的数据。将来自 MBTI 问卷调查的结果与来自分类器的结果进行比较,结果总结在下表中。
样本外同行分析分类器的准确性
实验的样本量相对较小,因此这些结果仅作为理解分类器效用的基线。预测结果不同于 MBTI 评分的预测结果,并且明显低于样本内的准确性。这一结果强调了这样一个事实,即性格预测是具有挑战性的,尤其是当只使用书面文本来形成预测时。
比较“相似”的 MBTI 类型
性格是由许多因素组成的马赛克,两个拥有相同 MBTI 类型的人的性格仍然可能彼此不同。我们通过比较具有相同的基于问卷的 MBTI 类型 ISFJ 的两个应答者的分类器结果来研究这个想法。
两个具有相同自报 MBTI 类型(ISFJ)的受访者的分类器预测。按作者分类的图表
分类器预测如上图所示,响应者 A 和 B 分别被预测为 ISFP 和 ISTJ。尽管两个候选人都是自我认定的内向敏感型,自由文本显示,回应者 A(78%)比回应者 B (58%)更内向,同样,回应者 B(73%)比回应者 A (52%)更倾向于敏感。
能够根据对一个维度的亲和力来估计个性揭示了更详细的个性景观。因此,通过评估自然语言,组织可以更好地快速识别个性差异,即使他们是相同的基本类型。
LinkedIn:公司简介映射
现在我们来看这个项目有趣的部分,你一直读到现在的原因。目标很简单,就是确定我们的分类器是否可以用于通过员工档案总结获得关于不同公司及其文化的见解。
我们决定尝试一个非常普遍的假设:
“咨询公司倾向于偏爱外向的人而不是内向的人”
收集了“四大”咨询公司(德勤、安永、毕马威和普华永道)雇员的个人资料摘要,然后通过性格分类器。在收集过程中施加了几个条件:
- 员工应该已经在公司工作了 3 年以上,这样他们的个性才是公司“成功”员工的真实反映。
- 员工应该在他们的个人资料中列出地区和行业,以过滤掉虚假账户。
- 简档摘要必须包含至少 50 个单词,以确保分类器有足够的数据用于预测。
满足这些条件的大约 2,500 个轮廓被收集,然后被处理。下图显示了样本中四大性格外向者的平均百分比。据估计,这四家公司都有超过 50%的外向者,这符合我们最初的假设。
四大咨询公司 LinkedIn 档案中的外向成分。按作者分类的图表
然而,基于 2,500 个样本规模,四家公司之间的差异很小(德勤为 57.8%,安永为 60.7%)。
为了了解外向者在每家公司的分布情况,所有外向者的资料从外向得分最高到最低排列。然后收集每家公司中排名最高的 100 名外向者(得分为 85%及以上),如下图所示。
大四中极端外向预测的构成。按作者分类的图表
从这个角度来看,与其他三家咨询公司相比,安永包含了更多极度外向的员工(近 40%)。这可能表明,安永在工作和员工构成方面更为传统,而毕马威似乎更为平衡。
一种推测可能是,随着咨询公司承担更多以技术为导向的工作,这些公司需要通过技术专业化部门来扩展其能力(例如德勤的德勤数字和毕马威的 Solution 49x)。这些部门倾向于雇佣更多的技术型(内向型)员工,这平衡了这些公司的内向-外向分布。
虽然上述结果取决于许多因素(分类器准确性可能是最重要的),但该分析强调了使用来自自由文本的个性估计作为帮助理解抽象主题(如公司文化和适合度)的手段的潜在好处。
结论
我们构建的最终特征反映了文学中使用的内容;以及我们认为是个人性格模式的副产品。使用 RF 分类器,我们发现积极的情感和情绪词汇(如感觉、爱和美丽)强烈反映了 T-F 维度。相比之下,判断和感知维度似乎与理性化和分析相关(如工作、时间、思考、需求和计划)。
我们还发现,与 MBTI 类型相比,逻辑回归实现了最高的样本内准确性;能够成功识别相似维度之间的细微差别。这种性能与文献中发现的性能相当。除了单词 n-grams 之外,还考虑了九个特征,但是它们没有为这项任务提供太多的预测能力。
在评估四大咨询公司对外向个人的偏好时,我们发现安永有明显更高的偏好,这是基于从他们的 LinkedIn 档案中分类的“极度外向”的员工。
经过思考,分类器似乎可以从文本中识别与个性相关的一些细微差别,但是,它的性能还可以改进,我们注意到大部分预测能力仅来自单词单字。如果也使用文本以外的信息,可能会得到更好的估计。对于公司简介映射的应用,可以使用与 LinkedIn 上使用的语言和功能更接近的数据集。因此,一般来说,个人资料的元特征,如联系数量、发帖频率、教育程度和性别,可能是更好的个性鉴别器(在专业背景下)。
一个交互式 Jupyter 笔记本,使用了文章中提到的 LR 分类器,供您参考。它从文本输入中估计 MBTI 类型,请随意下载代码并亲自尝试一下!
Github 链接:https://Github . com/wiredtoserve/data science/tree/master/personality detection
参考
[1]于尔科维奇,m,Šnajder,j,2018。Reddit:个性预测的金矿,收录于:第二届社交媒体中人们观点、个性和情绪的计算建模研讨会会议录。第 87-97 页。
[2]f .海利根,j . m .德瓦勒,1999 年。定义、测量和行为决定因素。Brüssel.自由大学“利奥·使徒”中心实习生
[3]f .迈尔塞、m .沃克等人,2006 年。单词标志着书呆子:通过语言进行人格识别的计算模型,收录于:认知科学学会年会论文集。
[4]f .迈尔塞,沃克,M.A .,梅尔,M.R .,摩尔,R.K .,2007 年。使用语言线索自动识别对话和文本中的个性。人工智能研究杂志 30,457–500。
[5] Mehl,M.R .,Gosling,S.D .,Pennebaker,J.W .,2006 年。自然环境中的人格:日常生活中人格的表现和隐含的民间理论。人格与社会心理学杂志 90,862。
[6]穆罕默德,S.M .,特尼,博士,2013 年。Nrc 情感词典。加拿大国家研究委员会。2014 年,凯西亚特区,D.O .的 Tchokni,Sé aghdha。表情符号和短语:社会媒体中的地位象征,载于:第八届网络日志和社会媒体国际 AAAI 会议。
基于 Bert 和通用语句编码器的 Spark NLP 文本分类
在 Spark NLP 中使用 Bert 和通用句子编码器训练 SOTA 多类文本分类器,只需不到 10 分钟的几行代码。
由 Unsplash 上的absolute vision拍摄
自然语言处理(NLP)是许多数据科学系统中的关键组件,必须理解或推理文本。常见的用例包括文本分类、问题回答、解释或总结、情感分析、自然语言 BI、语言建模和消歧。
NLP 在越来越多的人工智能应用中是必不可少的。如果你正在构建聊天机器人,搜索专利数据库,将患者与临床试验相匹配,给客户服务或销售电话评分,从财务报告中提取事实或解决这些17 个行业的 44 个用例中的任何一个,从自由文本中提取准确的信息是必不可少的。
文本分类是现代自然语言处理中的主要任务之一,它的任务是给一个句子或文档分配一个合适的类别。类别取决于所选的数据集,范围可以是主题。
每个文本分类问题都遵循相似的步骤,并且用不同的算法来解决。更不用说像随机森林或逻辑回归这样经典和流行的机器学习分类器了,针对各种文本分类问题提出的深度学习框架超过 150 个。
文本分类问题中使用了几个基准数据集,最新的基准可以在nlpprogress.com上追踪。这是关于这些数据集的基本统计数据。
文本分类基准数据集
简单的文本分类应用程序通常遵循以下步骤:
- 文本预处理和清理
- 特征工程(从文本创建手工特征)
- 特征矢量化(TfIDF、计数矢量化器、编码)或嵌入(word2vec、doc2vec、Bert、Elmo、句子嵌入等。)
- 用 ML 和 DL 算法训练模型。
Spark 自然语言处理中的文本分类
在本文中,我们将使用通用句子嵌入在 Spark NLP 中构建一个文本分类模型。然后我们将它与其他 ML 和 DL 方法以及文本矢量化方法进行比较。
Spark NLP 中有几个文本分类选项:
- Spark NLP 中的文本预处理和使用来自 Spark ML 的 ML 算法
- Spark ML 的 Spark NLP 和 ML 算法中的文本预处理和单词嵌入(Glove、Bert、Elmo)
- Spark ML 的 Spark NLP 和 ML 算法中的文本预处理和句子嵌入(通用句子编码器)
- Spark 自然语言处理中的文本预处理和分类器
应用的每个注释器都会向数据框添加一个新列,该数据框将被输入到管道中
以下是 Saprk NLP 中可用的单词/句子嵌入和语言模型。
Spark NLP 有 3 个接受句子嵌入的文本分类器
通用句子编码器(使用)
在自然语言处理(NLP)中建立任何深度学习模型之前,文本嵌入起着主要作用。文本嵌入将文本(单词或句子)转换成数字向量。
基本上,文本嵌入方法将单词和句子编码在固定长度的密集向量中,以显著改善文本数据的处理。这个想法很简单:出现在相同上下文中的单词往往有相似的意思。
像 Word2vec 和 Glove 这样的技术是通过将单词转换成矢量来实现的。因此“猫”的对应向量将比“鹰”更接近“狗”。但是在嵌入一个句子时,需要在这个向量中捕获整个句子的上下文以及单词。这就是“通用句子编码器”出现的地方。
通用语句编码器将文本编码成高维向量,这些向量可用于文本分类、语义相似性、聚类和其他自然语言任务。预训练的通用句子编码器在 Tensorflow-hub 中公开提供。它有两种变化,即一种用变压器编码器训练,另一种用深度平均网络(DAN) 训练。
Spark NLP 还使用了 Tensorflow-hub 版本的 use,它被封装在一种方式中,让它在 Spark 环境中运行。也就是说,您可以在 Spark NLP 中即插即用,以分布式方式训练模型。
使用为句子生成嵌入,而无需进一步计算,而不是对句子中每个单词的单词嵌入进行平均来获得句子嵌入。
基于 ClassifierDL 的文本分类及其在 Spark NLP 中的应用
在本文中,我们将使用文本分类任务中的基准数据集之一的 AGNews 数据集,使用版本 2.4.4 中添加到 Spark NLP 的最新分类模块 ClassifierDL annotator,在 Spark NLP 中构建一个文本分类器。
是 Spark NLP 中第一个多类文本分类器,它使用各种文本嵌入作为文本分类的输入。ClassifierDL
注释器使用深度学习模型(DNNs ),该模型内置于 TensorFlow 中,支持多达 50 个类。
也就是你可以用这个ClassiferDL
在 Spark NLP 中构建一个Bert
、Elmo
、Glove
、Universal Sentence Encoders
的文本分类器。
让我们开始编码吧!
开始加载必要的包并启动 Spark 会话。
import sparknlpspark = sparknlp.start()
# sparknlp.start(gpu=True) >> for training on GPUfrom sparknlp.base import *
from sparknlp.annotator import *
from pyspark.ml import Pipeline
import pandas as pdprint("Spark NLP version", sparknlp.version())print("Apache Spark version:", spark.version)>> Spark NLP version 2.4.5
>> Apache Spark version: 2.4.4
然后我们可以从 Github repo 下载[AGNews data set](http://groups.di.unipi.it/~gulli/AG_corpus_of_news_articles.html)
。
! wget [https://raw.githubusercontent.com/JohnSnowLabs/spark-nlp-workshop/master/tutorials/Certification_Trainings/Public/data/news_category_train.csv](https://raw.githubusercontent.com/JohnSnowLabs/spark-nlp-workshop/master/tutorials/Certification_Trainings/Public/data/news_category_test.csv)! wget [https://raw.githubusercontent.com/JohnSnowLabs/spark-nlp-workshop/master/tutorials/Certification_Trainings/Public/data/news_category_test.csv](https://raw.githubusercontent.com/JohnSnowLabs/spark-nlp-workshop/master/tutorials/Certification_Trainings/Public/data/news_category_test.csv)trainDataset = spark.read \
.option("header", True) \
.csv("news_category_train.csv")trainDataset.show(10, truncate=50)>>
+--------+--------------------------------------------------+
|category| description|
+--------+--------------------------------------------------+
|Business| Short sellers, Wall Street's dwindling band of...|
|Business| Private investment firm Carlyle Group, which h...|
|Business| Soaring crude prices plus worries about the ec...|
|Business| Authorities have halted oil export flows from ...|
|Business| Tearaway world oil prices, toppling records an...|
|Business| Stocks ended slightly higher on Friday but sta...|
|Business| Assets of the nation's retail money market mut...|
|Business| Retail sales bounced back a bit in July, and n...|
|Business|" After earning a PH.D. in Sociology, Danny Baz...|
|Business| Short sellers, Wall Street's dwindling band o...|
+--------+--------------------------------------------------+
only showing top 10 rows
AGNews
数据集有 4 个类:World, Sci/Tech, Sports, Business
from pyspark.sql.functions import coltrainDataset.groupBy("category") \
.count() \
.orderBy(col("count").desc()) \
.show()
>>
+--------+-----+
|category|count|
+--------+-----+
| World|30000|
|Sci/Tech|30000|
| Sports|30000|
|Business|30000|
+--------+-----+testDataset = spark.read \
.option("header", True) \
.csv("news_category_test.csv")testDataset.groupBy("category") \
.count() \
.orderBy(col("count").desc()) \
.show()>>
+--------+-----+
|category|count|
+--------+-----+
|Sci/Tech| 1900|
| Sports| 1900|
| World| 1900|
|Business| 1900|
+--------+-----+
现在我们可以将这个数据帧提供给 Spark NLPdocument assembler,它是任何 Spark 数据报的 Spark NLP 的入口点。
# actual content is inside description columndocument = DocumentAssembler()\
.setInputCol("description")\
.setOutputCol("document")
# we can also use sentence detector here
# if we want to train on and get predictions for each sentence# downloading pretrained embeddings
use = UniversalSentenceEncoder.pretrained()\
.setInputCols(["document"])\
.setOutputCol("sentence_embeddings")# the classes/labels/categories are in category columnclasssifierdl = ClassifierDLApproach()\
.setInputCols(["sentence_embeddings"])\
.setOutputCol("class")\
.setLabelColumn("category")\
.setMaxEpochs(5)\
.setEnableOutputLogs(True)use_clf_pipeline = Pipeline(
stages = [
document,
use,
classsifierdl
])
仅此而已。我们获取数据集,输入,然后从USE
获取句子嵌入,然后在ClassifierDL
中训练。零文本预处理或清洗!
现在我们开始训练(试衣)。我们将使用ClassiferDL
中的.setMaxEpochs()
参数训练 5 个时期。在Colab
环境中,大约需要 10 分钟才能完成。
use_pipelineModel = use_clf_pipeline.fit(trainDataset)
当您运行这个时,Spark NLP 会将训练日志写到您主目录中的annotator_logs
文件夹。以下是您阅读日志的方法。
正如你所看到的,我们在不到 10 分钟的时间内达到了 90%以上的验证准确率,没有进行文本预处理,这通常是任何 NLP 建模中最耗时和费力的步骤。
现在让我们看看test set
上的预测。我们将使用上面下载的测试集。
下面是我们如何通过sklearn
库中的classification_report
获得测试指标。
我们达到了 89.3%的测试集准确率!看起来不错!
Spark 自然语言处理中嵌入 Bert 句子的文本分类
(在 2.6.0 版本之后,Spark NLP 引入了 *BertSentenceEmbeddings*
注释器和 30 多个针对伊莱克特和伯特的预训练句子嵌入模型,大小不一。)
Spark NLP Bert 模型中的命名约定。l 表示在产生嵌入时使用哪个池层,H 表示返回的嵌入的维度。点击查看完整列表。
现在让我们使用BertSentenceEmbeddings
在同一个数据集上训练一个模型。
用一个小伯特句子嵌入代替使用
我们只是加载了一个小的具有L8
和512
维度的 Bert 句子嵌入,并使用它来代替USE.
,正如你所看到的,它的大小几乎是USE
的八分之一,具有 Bert 的强大功能。这是我们在训练后得到的。
几乎与我们从USE
获得的指标相同。就像 AutoML on scale 一样,只需要几行代码!
Spark 自然语言处理中文本预处理的文本分类
正如在任何文本分类问题中的情况一样,有许多有用的文本预处理技术,包括词汇化、词干化、拼写检查和停用词移除,Python 中几乎所有的 NLP 库都有应用这些技术的工具 ,除了拼写检查 。目前,Spark NLP 库是唯一一个现成的具有拼写检查功能的 NLP 库。
让我们在 Spark NLP 管道中应用这些步骤,然后用 Glove 单词嵌入训练一个文本分类器。我们将首先应用几个文本预处理步骤(通过仅保留字母、移除停用词、然后移除引理来标准化),然后获得每个令牌的单词嵌入(令牌的引理),然后对每个句子中的单词嵌入进行平均,以获得每行的句子嵌入。
关于 Spark NLP 中的所有这些文本预处理工具,您可以在这个 Colab 笔记本中找到详细的说明和代码示例。
然后我们就能坐上火车了。
clf_pipelineModel = clf_pipeline.fit(trainDataset)
并获得测试指标。
现在我们已经有 88%的测试集准确率了!即使在所有这些文本清理步骤之后,我们也无法击败Universal Sentence Embeddings + ClassifierDL
😃,这主要是因为与清理后的版本相比,USE
通常在原始文本上表现更好,因为它已经在原始句子上进行了训练,当我们应用文本预处理时,我们引入了一些在USE
被训练时没有看到的noise
,并且句子一致性在清理时受到了损害。
为了用BERT
训练同一个分类器,我们可以在上面建立的同一个流水线中用bert_embeddings
级代替glove_embeddings
级。你可以在这个链接找到更多关于我们如何在 Spark NLP 中实现和利用Bert
的信息。
word_embeddings = BertEmbeddings\
.pretrained('bert_base_cased', 'en') \
.setInputCols(["document",'lemma'])\
.setOutputCol("embeddings")\# we can also use Elmo embeddings instead.word_embeddings = ElmoEmbeddings\
.pretrained('elmo', 'en')\
.setInputCols(["document",'lemma'])\
.setOutputCol("embeddings")
使用 LightPipeline 进行更快速的推断
【Spark ML 管道是否转换为单机但多线程的任务,对于较小的数据量,速度提高了 10 倍以上(小是相对的,但 50k 句子大约是一个很好的最大值)。要使用它们,我们只需插入一个经过训练的(合适的)管道,然后注释一个纯文本。我们甚至不需要将输入文本转换成数据帧,就可以将它输入到接受数据帧作为输入的管道中。当从一个训练好的 ML 模型中获得几行文本的预测时,这个特性将非常有用。
光管道很容易创建,也让你不用处理 Spark 数据集。它们的速度也非常快,并且只在驱动节点上工作时,它们执行并行计算。让我们看看它是如何应用到我们上面描述的例子中的:
light_model = LightPipeline(clf_pipelineModel)text="Euro 2020 and the Copa America have both been moved to the summer of 2021 due to the coronavirus outbreak."light_model.annotate(text)['class'][0]>> "Sports"
您还可以将这个训练好的模型保存到您的磁盘上,然后在另一个带有ClassifierDLModel.load()
的 Spark 管道中使用。
结论
在本文中,我们使用流行的单词嵌入和通用句子编码器在 Spark NLP 中训练了一个多类文本分类模型,然后在不到 10 分钟的训练时间内获得了不错的模型精度。完整的代码可以在这个 Github repo (Colab 兼容)找到。我们还准备了另一个笔记本来涵盖 Spark NLP 和 Spark ML 中几乎所有可能的文本分类组合(CV、TfIdf、Glove、Bert、Elmo、USE、LR、RF、ClassiferDL、DocClassifier)。
我们还开始为公共和企业(医疗保健)版本提供在线 Spark NLP 培训。这里是所有公开的 Colab 笔记本的链接,它将带你在几个小时内一步一步地从零到英雄。
John Snow Labs 将组织虚拟 Spark NLP 培训,以下是下一次培训的链接:
【https://events.johnsnowlabs.com/online-training-spark-nlp 号
我们希望您已经阅读了我们官方媒体页面上的前几篇文章,加入了我们的 slack 频道,并开始玩 Spark NLP。以下是其他文章的链接。别忘了关注我们的页面,敬请期待!
Spark NLP 中带 BERT 的命名实体识别(NER)
Spark NLP 中使用的文本分类的整个管道
亚马逊美食评论的分类
我们如何对客户的反馈进行分类?
照片由克里斯蒂安·威迪格在 Unsplash 上拍摄
这里的目标是根据顾客的文本对食品评论进行分类。所以第一步是下载数据集。对于供应商来说,利用客户的评论来为他们提供更好的服务是非常有趣的。评论包括几个功能,如“产品 Id”、“用户 Id”、“分数”和“文本”。
但是我们如何分析这类问题呢?
数据
让我们看看数据集中包含的要素:
print(“There are **{}** observations and **{}** features in this dataset. “\ .format(reviews.shape[0],reviews.shape[1]))There are 568454 observations and 10 features in this dataset.
如图所示,该数据集由 568454 篇评论组成,时间跨度超过十年。在这个数据集中有十种不同的特征,但是我们只分别将“文本”和“分数”列作为输入/输出。
我们将根据客户的短信预测分数。这叫做情感分析。其他特征不影响目标和特征之间的关系。
现在,让我们看看数据集中包含的要素:
reviews.head()
数据探索
为了更好地理解目标(分数)分布,我们使用。seaborn 图书馆的 countplot。通过应用这个导入的函数,我们可以看到分数的分布。
**import** **seaborn** **as** **sns**
**import** **matplotlib.pyplot** **as** **plt**
sns.countplot(reviews[‘Score’])
plt.show()
它表明分数五在其余的分数之间有最大的数。基于分数分布,在我们的例子中,我们将它作为我们的目标,它显示分布是偏斜的,我们还可以考虑应用对数使它看起来更像高斯分布。当输入和输出具有高斯分布时,机器学习工作得更好。
让我们看看熊猫的每个特征的类型。info()函数,它也可以让您对特性有一个大致的了解。了解每一列是否有任何缺失值会很有帮助,这样您就能够以有效的方式处理它。
reviews.info()
由于我们只处理两列,并且没有缺失值,我们不需要删除任何观察值或使用插补转换器来完成缺失值。
文本表示
多项式朴素贝叶斯分类器适用于文本分类。基于数据集的大小和创建离散特征,我认为使用运行速度快的机器学习算法是可行的。否则,需要一段时间才能得到满意的输出。
我们的目标是找到输入和输出之间的关系。我使用“文本”作为输入,使用“分数”作为输出。所以我的第一步是把文本转换成向量。我们必须这样做,因为我们对多项式模型使用朴素贝叶斯分类器,而且这种方法也接受数字输入。这与应用任何机器学习算法的技术相同。
**from** **sklearn.feature_extraction.text** **import** TfidfTransformer
**from** **sklearn.naive_bayes** **import** MultinomialNB
**from** **sklearn.pipeline** **import** Pipeline
我们需要将反馈(文本)转换为令牌计数矩阵,以便从文本内容中提取数字特征。这样,计数的稀疏表示就产生了。这是非常有益的,因为你可以想象独特的映射词到向量创建一个矩阵的巨大规模。CountVectorizer 用于此目的,它可以按如下方式导入。
**from** **sklearn.feature_extraction.text** **import** CountVectorizer
有许多像“the”、“them”、“are”这样的词对上下文的意思没有任何影响,这些词被称为停用词。它们不提供信息,可以通过选择 step_words='english '作为 CountVectorizer 函数中的超参数来删除。
下一步是使用 tf-idf 表示来规范化计数矩阵。标准化频率而不是使用原始频率的主要原因是为了减少在文本中出现几次的标记的影响,并且没有出现几次的标记信息量大。例如,单词“document”在给定的语料库中出现一千次,而“awesome”出现两次。tf-idf 在这种情况下工作,就像预处理数据,将原始特征向量变成更适合机器学习算法的表示。
**from** **sklearn.feature_extraction.text** **import** TfidfTransformer
管道
我使用管道功能来完成所有步骤。顺序应用变换列表和最终估计器。因此,它开始应用 CountVectorizer、Tfidf,然后是 MultinomialNB。您可以拥有管道工作所需的任意数量的变压器。
twitter_sentiment = Pipeline([('CVec', CountVectorizer(stop_words='english')),
('Tfidf', TfidfTransformer()),
('MNB', MultinomialNB())])
交叉验证
最后,cross_validate 与 roc_auc 指标一起使用。应用 k-fold 交叉验证,并且以循环中值的平均值来计算性能度量。在我们的例子中,k 被指定为 5。5 和 10 是在 k 倍交叉验证中选择的常见值。下面是这段代码。
**from** **sklearn.model_selection** **import** cross_validatecv_pred = cross_validate(twitter_sentiment,
reviews['Text'],
reviews['Score'],
cv=5,
scoring=('roc_auc_ovr'), n_jobs=-1, verbose =10)
ROC-AUC
这是一个分类问题,所以我们用受试者工作特征曲线下的完全面积(ROC-AUC)来衡量预测得分。因为我们有一个多类的问题,我们要去' roc_auc_ovr '。对于这种情况,可以使用其他选项。我们使用 ROC_AUC 测量的结果如下。
cv_pred['test_score']array([0.80588185, 0.81448439, 0.8088359 , 0.81728556, 0.81103624])
结论
每折一次,分数都在 80%以上。有可用的算法,如随机森林,梯度推进也可以在这里使用。但这取决于你在解决每一个案件中寻找什么。你在寻找一个快速的答案和更好的准确性吗?这两个选项之间总是有一个权衡,您需要为每个数据集选择最佳选项。
完整的代码可以通过这个链接访问。
基于 LSTM 和单词嵌入的灾难微博文本分类
单词嵌入对文本分类准确率的影响
https://www . istock photo . com/photo/disaster-area-with tornado-GM 172448852-23567577
这是我的第一个 Kaggle 笔记本,我想为什么不把它也写在介质上呢?
我的 Github 上的全部代码。
在这篇文章中,我将详细说明如何使用 fastText 和 GloVe 作为文本分类的 LSTM 模型上的单词嵌入。我在做关于自然语言生成的论文时对单词嵌入产生了兴趣。结果表明,嵌入层权重的嵌入矩阵提高了模型的性能。但由于是在 NLG,这种衡量是主观的。我也只用了快速文本。所以在这篇文章中,我想看看每种方法(有快速文本和手套以及没有)对预测的影响。在我的 Github 代码上,我也将结果与 CNN 进行了比较。我在这里使用的数据集来自 Kaggle 上的一个比赛,由推文组成,并标有推文是否使用灾难性的词语来通知真实的灾难,或者只是隐喻性地使用它。老实说,在第一次看到这个数据集时,我立即想到了 BERT 及其理解能力,这比我在本文中提出的要好得多。
但是无论如何,在这篇文章中,我将集中讨论 fastText 和 GloVe。
走吧。
数据+预处理
这些数据由 7613 条带有标签(专栏目标)的推文(专栏文本)组成,无论它们是否在谈论一场真正的灾难。其中 3271 行通知真实灾难,4342 行通知非真实灾难。kaggle 竞赛上分享的数据,如果你想了解更多的数据,你可以在这里阅读。
课文中真实灾难词的例子:
“拉朗格萨斯克附近的森林失火。加拿大"
使用灾难词但不谈论灾难的例子:
“这些箱子准备爆炸!爆炸小猫终于来了!小猫游戏#探索小猫"
数据将被分为训练(6090 行)和测试(1523 行),然后进行预处理。我们将只使用文本和目标列。
from sklearn.model_selection import train_test_splitdata = pd.read_csv('train.csv', sep=',', header=0)train_df, test_df = train_test_split(data, test_size=0.2, random_state=42, shuffle=True)
此处使用的预处理步骤:
- 案例折叠
- 清洗停止字
- 符号化
from sklearn.utils import shuffleraw_docs_train = train_df['text'].tolist()
raw_docs_test = test_df['text'].tolist()
num_classes = len(label_names)processed_docs_train = []for doc in tqdm(raw_docs_train):
tokens = word_tokenize(doc)
filtered = [word for word in tokens if word not in stop_words]
processed_docs_train.append(" ".join(filtered))processed_docs_test = []for doc in tqdm(raw_docs_test):
tokens = word_tokenize(doc)
filtered = [word for word in tokens if word not in stop_words]
processed_docs_test.append(" ".join(filtered))tokenizer = Tokenizer(num_words=MAX_NB_WORDS, lower=True, char_level=False)
tokenizer.fit_on_texts(processed_docs_train + processed_docs_test) word_seq_train = tokenizer.texts_to_sequences(processed_docs_train)
word_seq_test = tokenizer.texts_to_sequences(processed_docs_test)
word_index = tokenizer.word_indexword_seq_train = sequence.pad_sequences(word_seq_train, maxlen=max_seq_len)word_seq_test = sequence.pad_sequences(word_seq_test, maxlen=max_seq_len)
文字嵌入
步骤一。下载预先训练好的模型
使用快速文本和手套的第一步是下载每个预先训练好的模型。我用 Google Colab 防止笔记本电脑使用大内存,所以用请求库下载,直接在笔记本上解压。
我使用了两个单词嵌入中最大的预训练模型。fastText 模型给出了 200 万个单词向量(600B 个标记),GloVe 给出了 220 万个单词向量(840B 个标记),两者都是在普通爬行上训练的。
fastText 预培训下载
import requests, zipfile, iozip_file_url = “https://dl.fbaipublicfiles.com/fasttext/vectors-english/wiki-news-300d-1M.vec.zip"r = requests.get(zip_file_url)z = zipfile.ZipFile(io.BytesIO(r.content))z.extractall()
手套预培训下载
import requests, zipfile, iozip_file_url = “http://nlp.stanford.edu/data/glove.840B.300d.zip"r = requests.get(zip_file_url)z = zipfile.ZipFile(io.BytesIO(r.content))z.extractall()
第二步。将预训练模型加载到单词向量中
FastText 给出了加载单词向量的格式,所以我用它来加载两个模型。
embeddings_index = {}f = codecs.open(‘crawl-300d-2M.vec’, encoding=’utf-8')
# for Glove
# f = codecs.open(‘glove.840B.300d.txt’, encoding=’utf-8')for line in tqdm(f):values = line.rstrip().rsplit(‘ ‘)word = values[0]coefs = np.asarray(values[1:], dtype=’float32')embeddings_index[word] = coefsf.close()
第三步。嵌入矩阵
对于训练数据中每个单词的权重,将在嵌入层使用嵌入矩阵。它是通过枚举训练数据集中存在于标记化单词索引中的每个唯一单词,并使用来自 fastText orGloVe 的权重来定位嵌入权重(更多关于嵌入矩阵))。
但是有一种可能性是,有些词不在向量中,比如错别字、缩写或用户名。这些单词将被存储在一个列表中,我们可以比较 fastText 和 GloVe 处理单词的性能
words_not_found = []nb_words = min(MAX_NB_WORDS, len(word_index)+1)
embedding_matrix = np.zeros((nb_words, embed_dim))for word, i in word_index.items():
if i >= nb_words:
continue
embedding_vector = embeddings_index.get(word)
if (embedding_vector is not None) and len(embedding_vector) > 0:
embedding_matrix[i] = embedding_vector
else:
words_not_found.append(word)print('number of null word embeddings: %d' % np.sum(np.sum(embedding_matrix, axis=1) == 0))
fastText 上的空词嵌入数是 9175,GloVe 上是 9186。可以假设 fastText 处理更多的单词,即使预训练是在较少的单词上训练的。
长短期记忆(LSTM)
你可以对超参数或架构进行微调,但我将使用非常简单的一个嵌入层,LSTM 层,密集层和辍学层。
from keras.layers import BatchNormalization
import tensorflow as tfmodel = tf.keras.Sequential()model.add(Embedding(nb_words, embed_dim, input_length=max_seq_len, weights=[embedding_matrix],trainable=False))model.add(Bidirectional(LSTM(32, return_sequences= True)))
model.add(Dense(32,activation=’relu’))model.add(Dropout(0.3))
model.add(Dense(1,activation=’sigmoid’))model.summary()
from keras.optimizers import RMSprop
from keras.callbacks import ModelCheckpoint
from tensorflow.keras.callbacks import EarlyStoppingmodel.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])es_callback = EarlyStopping(monitor='val_loss', patience=3)history = model.fit(word_seq_train, y_train, batch_size=256, epochs=30, validation_split=0.3, callbacks=[es_callback], shuffle=False)
结果
fastText 以大约 83%的准确度给出了最好的性能,而 GloVe 给出了 81%的准确度。性能上的差异并不显著,但是与没有单词嵌入的模型的性能(68%)相比,我们可以看到单词嵌入在嵌入层权重上的显著使用。
fastText 单词嵌入的准确性
手套单词嵌入的准确性
无单词嵌入的准确性
关于训练性能的更多信息,详细代码,如果你想在不同的数据集上应用它,你可以在我的 GitHub 上看到完整代码。
感谢您的阅读!
文本分类:监督和非监督学习方法
使用文本特征和主题建模的文本分类探索,从概念到部署
来自动机文本的单词特征。(图片由作者提供)
一年半以前,在参加一个数据黑客马拉松时,我偶然发现 Python 和 Anaconda 是数据科学(DS)的工具。无数的 Python 库强调了 Python 作为数据科学工具包的多功能性。遗憾的是,当时我不具备使用 Python 的必要技能。此后,我参加了 Python 和 SQL 的在线课程,并逐渐对 DS 领域产生了兴趣。为了加快向 DS 的过渡,我参加了大会的数据科学沉浸式(DSI)计划。DSI 是一个为期 3 个月的强化训练营,旨在通过一系列项目的体验式学习,让学生掌握基本的数据科学知识和技能。在这篇文章中,我希望通过我的关于通过动机(intel)文本对恐怖爆炸袭击进行分类的 capstone 项目,分享几个作为 DS 初学者的学习点。
- 规划很重要,但灵活性也很重要— 对于 capstone,我们可以自由选择主题。然而,当我们提出我们最初选择的主题和问题陈述时,可能不会立即显而易见的是作为 DS 项目的主题需要由数据的可用性来支持。我最初选择的数据集一周后才可用(Kaggle 竞赛)。听从导师的指导,在决定 Kaggle 上的【GTD】全球恐怖主义数据集之前,我研究了 UCI 和 Kaggle 数据仓库。我选择这个主题是因为 1)我想在做一些稍微不同的事情时重温 NLP 技术,2)探索 潜在狄利克雷分配(LDA)的潜力;一个无监督的 NLP 主题建模,作为分类的监督学习输入 。
- 尝试维护建模工作流程的概述— 可视化建模工作流程有助于项目组织,并确保项目交付的一致性。在其他任务中,当试图在完成顶点工作的同时平衡高级主题时,更是如此。它还帮助节省了宝贵的时间,将一个人在机器学习方法上的思维过程转化为一种可以与导师和同学轻松交流的格式。节省下来的时间用于模型部署,这也带来了许多惊喜和学习机会。建模工作流也有助于分配时间来重温以前的技术和尝试新事物(例如,动画,图像遮罩,主题模型,模型部署)。让我们深入研究一下技术细节!
模型工作流程(图片由作者提供)
数据集和探索性数据分析(EDA)
Kaggle 上有几个关于全球恐怖主义的数据集。对于 capstone,我使用了从 1970 年到 2017 年的数据集。由于数据集的大小,我使用了 Git 大文件存储 来管理文件到 Github 的推送。数据集以 ISO-8859–1 格式编码,读取文件时需要指定该格式。谢天谢地,这是 Kaggle 上讨论的少数几个话题之一。但是,如果遇到另一种格式但不确定其编码,该怎么办呢?Agnese Poikane-Puchok 的这个 方法 应该会派上用场。该数据集包含 181,691 个条目和 135 个变量,描述了特定恐怖事件的细节。作为一个 NLP 项目,感兴趣的主要特征是描述任何恐怖事件的背景、犯罪者、目标和信息来源的动机文本。我使用spaCy来可视化动机文本中的识别实体和短语结构。措辞的一致性有助于建立将其用作文本分类输入的信心。它还展示了良好数据质量的重要性。然而,由于动机文本是 1997 年后引入的一个特征,在数据清理后,只有 32,521 个条目具有足够的文本数据用于文本分类。
动态文本空间实体识别实例(作者图片)
EDA 中使用的其他变量有助于了解全球恐怖主义趋势,从最活跃的团体到最受欢迎的袭击形式以及恐怖团体的潜在动机。其他可视化和 EDA 变量选择的基本原理可以在这里访问(自述:p03_dataclean_ed,Jupyter 笔记本:P5.0 数据清理,EDA,Feature engineering.ipynb )。这里提取了一些与文本分类的特征工程直接相关的视觉效果进行说明。
1970 年至 2017 年全球恐怖事件(图片由作者提供)
受 【汉斯·罗斯林】 作品的启发,我探索用动画作为一种手段,从空间和时间的角度来可视化恐怖事件。我最初使用 ImageMagick 用于。但无法解决技术问题(生成的。gif 文件自删除),因此,可以依靠第三方软件来实现。gif 文件。直观地看一下这些年来各种攻击方式的比例,轰炸仍然是最受欢迎的攻击方式,占所有攻击方式的 50%。
2010-2017 年恐怖袭击模式分布(图片由作者提供)
数据准备和文本分类器模型评估(第 1 部分)
我将构建一个分类器模型,将动机文本作为输入,输出一个二元分类(炸弹或非炸弹攻击)。我把工程爆炸袭击列为正面类,其他袭击列为负面类。这个职业相对平衡,炸弹攻击占 48%。数据被 80-20 分成训练/测试数据。然后,将训练数据进一步分成 80–20 个训练和验证子集。对于分类器模型,我探索了逻辑回归和朴素贝叶斯模型。结果(基于验证(保持)数据集)总结如下。逻辑回归模型是基于敏感性(目的是减少假阴性,当它们是实际爆炸时预测非爆炸袭击)和 ROC AUC 的更好的执行模型。
基于验证(保留)数据集的模型指标(图片由作者提供)
选择逻辑回归模型作为生产模型,我探索了模型调优,比如检查和删除导致假阴性分类的单词,以及删除重叠的单词。结果总结如下。
基于测试数据集的生产模型性能(图片由作者提供)
主题建模
企业通常使用主题建模和主题分类来从非结构化数据中获取见解(例如,客户情感)。这两者之间的区别:
- 主题建模是一种无监督的机器学习方法,它分析文本数据并为一组文档确定聚类词。
- 主题分类是一种有监督的机器学习方法。文本数据被预先标记,以便主题分类器可以基于从标记数据中学习到的模式进行分类。
LDA 是一种主题建模方法。虽然它在从文档中发现单词簇(主题)方面很有用,但它没有与文本分类器模型集成。由于数据集的原因,用于主题分类的数据的审查和手动标记是站不住脚的。此外,我不具备必要的主题专业知识,无法根据动机文本确定特定恐怖事件可能属于的潜在主题。
LDA 输出作为分类问题的输入
Marc Kelechava 的关于使用 LDA 主题模型作为分类模型输入的文章起到了启发作用,并就如何将主题模型集成到文本分类器模型中提供了有价值的见解。在一个主题的每个单词簇的后面,是来自每个动机文本条目的主题的概率分布。如果在主题分布上训练的模型可以在看不见的数据上概括,我们可以推断该模型已经识别了仍然适用于新的看不见的数据的主题群(在这个上下文中的潜在动机)。
(图片由作者提供)
这些是我的步骤:
- 使用训练数据集的主题建模。
- 根据连贯性得分找到最佳的主题数量。
- 根据训练数据训练 LDA 模型。主题概率分布(TPD)随后被用作逻辑回归模型中的特征向量,用于对验证(拒绝)数据集进行二元分类(炸弹攻击与非炸弹攻击)。
- 使用训练好的 LDA 模型从测试数据中导出 TPD。
- 在 TPD 上运行逻辑回归模型,查看模型是否具有普遍性。
其中一个关键步骤是提取主题的特征向量。在我的例子中,主题的最佳数量是 41。
*# Make train Vectors
train_vecs = []
for i in range(len(X_train)):
top_topics = lda_train.get_document_topics(traincorpus[i], minimum_probability=0.0)
topic_vec = [top_topics[i][1] for i in range(41)]
train_vecs.append(topic_vec)*
该模型在测试数据集上具有更高的灵敏度和 ROC AUC!下一个问题自然是“如果将来自 CountVectorizer 的记号和来自 LDA 的 TPD 结合起来,模型性能会如何?”
评估生产模型(图片由作者提供)
文本分类器模型评估(下)
由于 CountVectorizer 的输出是令牌计数的稀疏矩阵,因此需要进行一些转换来连接 LDA 输出。有许多方法可以做到这一点。我是这样做的:
*# Convert sparse matrix to dense
X_tr_dense = X_train_cleancv.toarray()
X_ts_dense = X_test_cleancv.toarray()# add numpy array (train and test topic model vectors to dense matrix)
X_tr_dense_tm = np.concatenate((X_tr_dense,X_tr_vec),axis=1)
X_ts_dense_tm = np.concatenate((X_ts_dense,X_ts_vec),axis=1)from scipy.sparse import csr_matrix
# Convert back to sparse matrix for modeling
X_tr_sparse = csr_matrix(X_tr_dense_tm)
X_ts_sparse = csr_matrix(X_ts_dense_tm)*
在此之后,模型预测相对简单。各种方法的比较总结如下。虽然使用组合 TPD 和令牌的模型不如使用计数矢量器的模型执行得好,但它有潜力。
模型指标摘要(图片由作者提供)
模型部署
听从指导员的鼓励,尝试模型部署,我跳进它的权利。模型部署可以是一个令人兴奋的冒险,同时也是一件令人毛骨悚然的事情,尤其是对于像我这样的初学者来说。对于模型部署,我使用 Heroku。它是一个平台即服务(PaaS ),使开发人员能够在云中构建、运行和操作应用程序。下面的逐步演练概述了我在学习过程中的一些发现。希望对于那些寻求部署他们的模型的人来说,它能派上用场。遵循并修改代码以适应您自己的项目需求。
前期需要什么
- 安装 Git (如果你还没有;您现在可能已经安装了它)
- 安装 Heroku 命令行界面
- 建立一个 Heroku 帐户
- 你的模型的 html 页面(设计可能会花费大量的时间,所以你可能想先做这件事。)注:我给它取名为【index.html】
考虑您希望部署的模型如何操作。对于我的顶点,分类器模型接受一个清除了自定义停用词的文本列表。在讲师的指导下,我编写了 CountVectorizer、自定义停用词和生产模型。在 Jupyter:
**import pickle
# save model to disk
filename1 = 'finalized_model.sav'
pickle.dump(model_lr2, open(filename1, 'wb'))
filename2 = 'finalized_cv.sav'
pickle.dump(cv2, open(filename2, 'wb'))
filename3 = 'finalized_stopwords.sav'
pickle.dump(s_words2, open(filename3, 'wb'))**
设置虚拟环境
为了托管您的应用程序(您想要部署的模型),您还需要 flask 和 virtualenv。使用 Anaconda Navigator 或 Anaconda 提示符安装它们。然后首先在本地设置一个项目目录空间来托管项目。在我的例子中,我已经有了保存各种项目的主目录,所以代码略有不同。为了我的项目需要,我创建了虚拟环境并命名为 顶点 。在 Anaconda 提示符下:
**cd virtualenvs
virtualenv capstone
# activate env from Scripts folder
cd Scripts
activate.bat
# to deactivate
deactivate.bat**
安装 gunicorn (对于 Heroku),为您的环境冻结 python 模块,如下所示:
**pip install gunicorn
pip freeze >> requirements.txt**
根据自己的项目需求,可以考虑配置需求列表。因为我没有使用大部分模块,所以我手工削减了包(以减少下游的部署时间)。详情请参考 Github 上的 requirements.txt 文件。
在根文件夹中创建一个 nltk.txt 文件 ,用所使用的 nltk 模块填充它。这是供 Heroku-python nltk 使用的。
烧瓶脚本
创建一个名为 templates 的文件夹来存放 html 表单。
**mkdir templates**
将index.html移动到该文件夹中。将 pickle 文件复制到项目 virtualenv 文件夹的根目录下(即 顶点 ),以下简称根目录。
Python 脚本
在根目录中创建 python 脚本。这些脚本依赖于一个人的模型和功能流。更多详情请参考 Github 上的required fn . py和 service.py (其中我持有应用模型)。
本地原型
要运行该服务,请在 Anaconda 提示符下依次输入以下内容。URL 应该显示用户可以访问网页的位置,并根据需要实时调整网页。
**SET FLASK_APP=service.py
SET FLASK_DEBUG=1
flask run**
赫罗库
在根目录下创建一个 Procfile (没有扩展名!). Procfile 只需要一行。 文件名 是包含 flask 应用程序的 python 脚本的名称,flask 名称为 app 。
**# Within Procfile
web: gunicorn <filename>:<main method name>
# filename refers to the python script containing flask application with flask name app
# app = Flask(__name__)
# In this instance the line is
web: gunicorn service:app**
登录到Heroku
,使用 Anaconda 提示符创建应用程序并部署
**heroku login
heroku create
# two url returned will be returned, the latter is the Heroku git remote repo where the application lives # on Heroku. The former is the web url where the application will be once deployed.
git add .
git commit -m"message"
git push heroku master
# Ensure at least one instance of application is running
heroku ps:scale web=1
# Say hello to your application!
heroku open**
警告
当前部署的模型过于简单,因为它只接受单个输入(动机文本)。在实际操作中,不期望智能的分类正好落在模型上。对于部署模型的输入,我使用了与爆炸和非爆炸攻击相关的动机文本的相等混合。请前往这个站点进行测试。有些人可能想知道为什么不为用户输入创建一个输入空间?两个考虑因素:1)当前的模型设置允许将条目链接到地面真相,2)考虑到主题的敏感性,我希望避免游戏化。
总结和未来工作
我希望这是一个信息丰富的阅读,并对您的项目需求有用。我打算很快扩展以下领域的顶点:
- 探索附加特征对模型性能的影响(特征工程空间和时间方面,如按区域攻击、按十年攻击)。
- 探索 tfidf 矢量器和 spaCy 的使用。与 CountVectorizer 类似,TFIDFVectorizer 计算词频,但与语料库(文档集合)中的词频成反比。
- 扩展到多类分类。
从顶点主题选择到顶点代码和模型部署材料的思考过程在我的 GitHub 这里托管。
感谢阅读!
后记(2020 年 7 月 5 日):我确实认识到生成的。gif 有点小,很难阅读,我试图找出一种方法来保持生成的分辨率大小。gif(世界地图动画)。如果你有这样做的想法,如果你能提出这样做的方法,我将不胜感激。谢了。
基于朴素贝叶斯的文本分类:理论与实例
入门
在本文中,我解释了朴素贝叶斯的工作原理,并一步步用 Python 实现了一个多类文本分类问题。
作者创作的人物。
目录
- 简介
- 朴素贝叶斯算法
- 处理文本数据
- Python 中的工作示例(分步指南)
- 奖励:与模特同乐
- 结论
1.介绍
朴素贝叶斯分类器是基于贝叶斯定理的分类算法集合。它不是一个单一的算法,而是一个算法家族,所有算法都有一个共同的原则,即每一对被分类的特征都是相互独立的。
朴素贝叶斯分类器被大量用于文本分类和文本 分析机器学习问题。
文本分析是机器学习算法的主要应用领域。然而,原始数据、符号序列(即字符串)不能直接提供给算法本身,因为大多数算法期望具有固定大小的数字特征向量,而不是具有可变长度的原始文本文档。
在这篇文章中,我将解释 a)朴素贝叶斯如何工作,b)我们如何使用文本 T34,数据 T35,数据 T36,在将它们转换成更合适的形式后,将它们放入模型 T40。最后,我用 Python 一步步实现一个多类文本分类问题。
我们开始吧!!!
如果你想在交互式路线图和活跃的学习社区的支持下自学数据科学,看看这个资源:https://aigents.co/learn
2.朴素贝叶斯算法
朴素贝叶斯分类器是基于贝叶斯定理的分类算法集合。它不是一个单一的算法,而是一个算法家族,所有算法都有一个共同的原则,即每一对被分类的特征都是相互独立的。
数据集分为两部分,即特征矩阵和响应/目标向量。
- 特征 矩阵 (X)包含数据集的所有向量(行),其中每个向量由相关特征的值组成。特征数为 d 即 X = (x1,x2,x2,xd)。
- 响应/目标 向量 (y)包含特征矩阵每行的类/组变量的值。
2.1。朴素贝叶斯的两个主要假设
朴素贝叶斯假设同一类的每个特征/变量构成一个:
- 独立
- 等于
对结果的贡献。
旁注:朴素贝叶斯所做的假设在现实世界的情况下一般不正确。事实上,独立性假设经常无法满足,这就是为什么它被称为“幼稚”的原因,也就是因为它假设了一些可能不真实的事情。
2.2.贝叶斯定理
贝叶斯定理是在已知一个事件发生的概率的情况下,求出另一个事件发生的概率。贝叶斯定理的数学表述如下:
作者创作的人物。
其中:
- A 和 B 称为事件。
- P(A | B)是事件 A 的概率,假设事件 B 为真(已经发生)。事件 B 也被称为证据。
- P(A)是 A 的先验(先验独立概率,即事件在证据被看到之前的概率)。
- P(B | A)是给定事件 A 的 B 的概率,即看到证据 A 后事件 B 的概率。
摘要
作者创作的人物。
2.3.朴素贝叶斯模型
给定一个数据矩阵 X 和一个目标向量 y,我们将我们的问题表述为:
作者创作的人物。
其中, y 为类变量, X 为维度为 d 的依赖特征向量,即 X = (x1,x2,x2,xd),其中 d 为样本的变量/特征个数。
- P(y|X)是给定样本 X 观察到类 y 的概率,其中 X = (x1,x2,x2,xd),其中 d 是样本的变量/特征的数量。
现在“天真的”条件独立性假设开始发挥作用:假设 X 中的所有特性都是相互独立,以类别 y 为条件:
作者创作的人物。
最后,为了找到给定的样本对于类变量 y 的所有可能值的概率,我们只需要找到具有最大概率的输出:
作者创作的人物。
3.处理文本数据
此时出现的一个问题如下:
我们如何使用原始文本数据来训练模型?原始数据是字符串的集合!
文本分析是机器学习算法的一个主要应用领域。然而,原始数据、符号序列(即字符串)不能直接提供给算法本身,因为大多数算法期望具有固定大小的数字特征向量,而不是具有可变长度的原始文本文档。
为了解决这个问题,scikit-learn 提供了从文本内容中提取数字特征的最常见方法的实用程序,即:
- 标记化字符串并为每个可能的标记给出一个整数 id,例如通过使用空格和标点符号作为标记分隔符。
- 统计每个文档中令牌的出现次数。
在此方案中,特征和样本定义如下:
- 每个单个令牌出现频率被视为一个特征。
- 给定文档的所有令牌频率的向量被视为多元样本。
“计数”示例(为了在我们继续之前真正理解这一点):
from sklearn.feature_extraction.text import CountVectorizer
corpus = [
'This is the first document.',
'This document is the second document.',
'And this is the third one.',
'Is this the first document?',
]vectorizer = CountVectorizer()
X = vectorizer.fit_transform(corpus)print(vectorizer.get_feature_names())
**[‘and’, ‘document’, ‘first’, ‘is’, ‘one’, ‘second’, ‘the’, ‘third’, ‘this’]**print(X.toarray())
[[0 1 1 1 0 0 1 0 1]
[0 2 0 1 0 1 1 0 1]
[1 0 0 1 1 0 1 1 1]
[0 1 1 1 0 0 1 0 1]]
在上面的玩具示例中,我们将一组字符串存储在变量语料库中。使用文本 转换器,我们可以看到我们的数据中有特定数量的唯一字符串(词汇)。
这可以通过打印矢量器. get_feature_names() 变量来看出。我们观察到我们有 9 个独特的单词。
接下来,我们打印转换后的数据( X )和,我们观察到以下:
- 我们在 X 中有 4 行作为我们的文本串的数量(我们在转换后有相同数量的样本)。
- 我们在转换后的数据( X ) f 或所有样本(转换前并非如此,即各个字符串具有不同的长度)中有相同数量的列(特征/变量)。
- 值 0,1,2 对出现在初始文本数据中的字的频率进行编码。
例如。第一个转换行是[0 1 1 1 0 1 0 1 0 1]和****唯一词汇是 ['and ',' document ',' first ',' is ',' one ',' second ',' The ',' this'],因此这意味着单词“document”,“first”,“is”,“the”和“this”在初始文本串中各出现 1 次(即“这是第一个文档”).
****边注:这是计数法。项-频率变换无非是将计数矩阵变换成归一化的项-频率矩阵。
希望现在一切都清楚了。如果没有,根据需要多次阅读这一段,以便真正掌握思想和理解这一转变。这是最基本的一步。
4.Python 中的工作示例
既然您已经理解了朴素贝叶斯和文本转换是如何工作的,那么是时候开始编码了!
问题陈述
作为一个工作示例,我们将使用一些文本** 数据,我们将构建一个朴素 贝叶斯模型来预测文本的类别。这是一个多类(20 类)文本分类问题**。****
让我们开始吧(我会带你走一遍)。首先,我们将加载所有必要的库:
import numpy as np, pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.datasets import fetch_20newsgroups
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import make_pipeline
from sklearn.metrics import confusion_matrix, accuracy_scoresns.set() # use seaborn plotting style
接下来,让我们加载数据** ( 训练和测试集合)😗*
# Load the dataset
data = fetch_20newsgroups()# Get the text categories
text_categories = data.target_names# define the training set
train_data = fetch_20newsgroups(subset="train", categories=text_categories)# define the test set
test_data = fetch_20newsgroups(subset="test", categories=text_categories)
让我们找出我们有多少类和样本:
print("We have {} unique classes".format(len(text_categories)))
print("We have {} training samples".format(len(train_data.data)))
print("We have {} test samples".format(len(test_data.data)))
上面的版画:
We have 20 unique classes
We have 11314 training samples
We have 7532 test samples
所以,这是一个 20 类文本分类问题用 n_train = 11314 训练 样本(文本句子)和 n _ test =****测试 样本(文本句子)。****
让我们想象一下第五个训练样本:
**# let’s have a look as some training data
print(test_data.data[5])**
如前所述,我们的数据是文本(更具体地说,是电子邮件),所以您应该会看到类似下面这样的输出:
作者创作的人物。
下一步包括建立朴素贝叶斯分类器,最后训练模型。在我们的例子中,我们将把文本文档的集合(训练和测试集)转换成一个令牌计数的矩阵(我在第 3 节中解释了这是如何工作的)。
为了实现文本转换,我们将使用 make_pipeline 函数。这将在内部转换文本数据,然后使用转换后的数据拟合模型。
**# Build the model
model = make_pipeline(TfidfVectorizer(), MultinomialNB())# Train the model using the training data
model.fit(train_data.data, train_data.target)# Predict the categories of the test data
predicted_categories = model.predict(test_data.data)**
代码的最后一行预测了测试集的标签。
让我们看看预测的类别名称:
**print(np.array(test_data.target_names)[predicted_categories])
array(['rec.autos', 'sci.crypt', 'alt.atheism', ..., 'rec.sport.baseball', 'comp.sys.ibm.pc.hardware', 'soc.religion.christian'], dtype='<U24')**
最后,让我们构建 多类混淆矩阵 来看看这个模型是好的还是这个模型只正确预测特定的文本类别。
**# plot the confusion matrix
mat = confusion_matrix(test_data.target, predicted_categories)
sns.heatmap(mat.T, square = True, annot=True, fmt = "d", xticklabels=train_data.target_names,yticklabels=train_data.target_names)
plt.xlabel("true labels")
plt.ylabel("predicted label")
plt.show()print("The accuracy is {}".format(accuracy_score(test_data.target, predicted_categories)))The accuracy is 0.7738980350504514**
作者创作的人物。
5.额外收获:和模特玩得开心
让我们用训练好的模型来找点乐子。让我们把我们喜欢的句子分类😄。
**# custom function to have fun
def my_predictions(my_sentence, model):
all_categories_names = np.array(data.target_names)
prediction = model.predict([my_sentence])
return all_categories_names[prediction] my_sentence = “jesus”
print(my_predictions(my_sentence, model))
['soc.religion.christian']my_sentence = "Are you an atheist?"
print(my_predictions(my_sentence, model))
['alt.atheism']**
我们将字符串“jesus”插入到模型中,它预测了类别“' soc.religion.christian']”。
把“我的句子”换成其他字符串玩模型😃。
6.结论
我们看到朴素贝叶斯对于多类文本分类问题是一个非常强大的算法。
边注 : 如果你想了解更多关于混淆矩阵(以及 ROC 曲线)的知识,请看这个:
** [## 用新冠肺炎假设的例子解释 ROC 曲线:二分类和多分类…
在这篇文章中,我清楚地解释了什么是 ROC 曲线以及如何阅读它。我用一个新冠肺炎的例子来说明我的观点,我…
towardsdatascience.com](/roc-curve-explained-using-a-covid-19-hypothetical-example-binary-multi-class-classification-bab188ea869c)
解读困惑矩阵
从上面的混乱 矩阵可以验证模型真的很好。
- 它能够正确地预测所有 20 类文本数据(大多数值在对角线上,少数不在对角线上)。
- 我们还注意到最高的误分类(偏离对角线的值)是 131 (从末尾起 5 行,右边最后一列)。值 131 意味着属于“宗教杂项”类别的 131 个文档被误分类为属于“宗教基督教”类别。
有趣的是,这两个类别非常相似,实际上人们可以将它们划分为一个更大的群体中的两个子群体,例如一般的“宗教”。
最后,测试集合上的准确度为 0.7739 对于一个 20 级的文本分类问题来说已经相当不错了🚀。
那都是乡亲们!希望你喜欢这篇文章。**
如果您喜欢这篇文章并觉得它有用,请关注👣我可以看到我所有的新帖子。
有问题吗?把它们作为评论贴出来,我会尽快回复。
我的个人资料(看看我收集的文章):
** [## Serafeim Loukas -走向数据科学
阅读 Serafeim Loukas 在《走向数据科学》中的文章。电气和计算机工程文凭(NTUA)。主人…
towardsdatascience.com](https://towardsdatascience.com/@seralouk)**
和我联系
您可能还喜欢:
** [## 支持向量机(SVM)解释清楚:分类问题的 python 教程…
在这篇文章中,我解释了支持向量机的核心,为什么以及如何使用它们。此外,我还展示了如何绘制支持…
towardsdatascience.com](/support-vector-machines-svm-clearly-explained-a-python-tutorial-for-classification-problems-29c539f3ad8) [## K-Means 聚类:工作原理&在数据中寻找最优的聚类数
数学公式,寻找最佳聚类数和 Python 中的工作示例。
towardsdatascience.com](/k-means-clustering-how-it-works-finding-the-optimum-number-of-clusters-in-the-data-13d18739255c) [## LSTM 时间序列预测:使用 LSTM 模型预测股票价格
在这篇文章中,我将向你展示如何使用预测 LSTM 模型来预测股票价格
towardsdatascience.com](/lstm-time-series-forecasting-predicting-stock-prices-using-an-lstm-model-6223e9644a2f) [## 时间序列预测:使用 ARIMA 模型预测股票价格
在这篇文章中,我将向你展示如何使用预测 ARIMA 模型来预测特斯拉的股票价格
towardsdatascience.com](/time-series-forecasting-predicting-stock-prices-using-an-arima-model-2e3b3080bd70) [## 最佳免费数据科学资源:免费书籍和在线课程
最有用的免费书籍和在线课程,适合想了解更多数据科学知识的人。
medium.com](https://medium.com/@seralouk/the-best-free-data-science-resources-free-books-online-courses-9c4a2df194e5) [## 用新冠肺炎假设的例子解释 ROC 曲线:二分类和多分类…
在这篇文章中,我清楚地解释了什么是 ROC 曲线以及如何阅读它。我用一个新冠肺炎的例子来说明我的观点,我…
towardsdatascience.com](/roc-curve-explained-using-a-covid-19-hypothetical-example-binary-multi-class-classification-bab188ea869c) [## 支持向量机(SVM)解释清楚:分类问题的 python 教程…
在这篇文章中,我解释了支持向量机的核心,为什么以及如何使用它们。此外,我还展示了如何绘制支持…
towardsdatascience.com](/support-vector-machines-svm-clearly-explained-a-python-tutorial-for-classification-problems-29c539f3ad8) [## PCA 清楚地解释了——如何、何时、为什么使用它以及特性的重要性:Python 指南
在这篇文章中,我解释了什么是 PCA,何时以及为什么使用它,以及如何使用 scikit-learn 在 Python 中实现它。还有…
towardsdatascience.com](/pca-clearly-explained-how-when-why-to-use-it-and-feature-importance-a-guide-in-python-7c274582c37e) [## 关于 Python 中的最小-最大规范化,您需要知道的一切
在这篇文章中,我将解释什么是最小-最大缩放,什么时候使用它,以及如何使用 scikit 在 Python 中实现它
towardsdatascience.com](/everything-you-need-to-know-about-min-max-normalization-in-python-b79592732b79) [## Scikit-Learn 的标准定标器如何工作
在这篇文章中,我将解释为什么以及如何使用 scikit-learn 应用标准化
towardsdatascience.com](/how-and-why-to-standardize-your-data-996926c2c832)**
使用 Python 中的单词嵌入和深度学习进行文本分类——对来自 Twitter 的推文进行分类
文本分类代码,以及对使用 Python 和 Tensorflow 所发生的事情的深入解释
本文的目的是帮助读者理解在创建文本分类器时如何利用单词嵌入和深度学习。
此外,文本建模中经常被忽略的部分,如什么是单词嵌入,什么是嵌入层或深度学习模型的输入,也将在这里讨论。
最后,所有概念的展示将在 Twitter 发布的数据集上付诸实践,该数据集关于一条推文是否是关于自然灾害的。
本文使用的主要技术是 Python 和 Keras API。
一个功能齐全的文本分类管道和来自 Twitter 的数据集可以在这里找到:https://github.com/Eligijus112/twitter-genuine-tweets。
本文中用到的 Word 嵌入文件可以在这里找到:https://nlp.stanford.edu/projects/glove/。
使用标记文本创建深度学习模型的管道如下:
- 将数据分为文本(X)和标签(Y)
- 预处理 X
- 从 X 创建一个单词嵌入矩阵
- 从 X 创建一个张量输入
- 使用张量输入和标签(Y)训练深度学习模型
- 对新数据进行预测
在本文中,我将逐一介绍这些步骤。本文的第一部分将使用一个小的示例数据集来涵盖所有的概念。文章的第二部分将把所有的概念实现到一个真实的例子中,这个例子是关于一条推文是否是关于自然灾害的。
使用文本进行预测的深度学习模型的主要构建模块是单词嵌入。
来自维基:单词嵌入是一组语言建模和特征学习技术的统称,在自然语言处理 (NLP)中,来自词汇表的单词或短语被映射到实数向量。例如,
“爸爸”= [0.1548,0.4848,1.864]
"妈妈" = [0.8785,0.8974,2.794]
简而言之,单词嵌入是表示字符串的数值向量。
实际上,单词表示是 100、200 或 300 维向量,它们在非常大的文本上被训练。
单词嵌入的一个非常重要的特征是,语义上相似的单词之间的距离(欧几里德距离、余弦距离或其他距离)比没有语义关系的单词之间的距离要小。例如,像“妈妈”和“爸爸”这样的词在数学上应该比“妈妈”和“番茄酱”或“爸爸”和“黄油”更接近。
单词嵌入的第二个重要特征是,当创建模型的输入矩阵时,无论我们在文本语料库中有多少独特的单词,我们在输入矩阵中都将有相同数量的列。与一次性编码技术相比,这是一个巨大的胜利,在一次性编码技术中,列的数量通常等于文档中唯一单词的数量。这个数字可以是几十万,甚至上百万。处理非常宽的输入矩阵在计算上要求很高。
举个例子,
想象一句话:克拉克喜欢在公园散步。
这里有 7 个独特的词。使用一个热编码向量,我们将每个单词表示为:
Clark = [1, 0, 0, 0, 0, 0, 0]
likes = [0, 1, 0, 0, 0, 0, 0]
to = [0, 0, 1, 0, 0, 0, 0]
walk = [0, 0, 0, 1, 0, 0, 0]
in = [0, 0, 0, 0, 1, 0, 0]
the = [0, 0, 0, 0, 0, 1, 0]
park = [0, 0, 0, 0, 0, 0, 1]
而如果使用二维单词嵌入,我们将处理以下向量:
Clark = [0.13, 0.61]
likes = [0.23, 0.66]
to = [0.55, 0.11]
walk = [0.03, 0.01]
in = [0.15, 0.69]
the = [0.99, 0.00]
park = [0.98, 0.12]
现在想象有 n 个句子。在一位热码编码的情况下,向量将呈指数增长,而单词的嵌入表示向量的大小将保持不变。这就是为什么在处理大量文本时,单词嵌入被用来表示单词、句子或整个文档。
使用具有一个输入层、一个隐藏层和一个输出层的神经网络来创建单词嵌入。
有关创建单词嵌入的更多信息,请访问文章:
[## 创建单词嵌入:使用深度学习在 Python 中编码 Word2Vec 算法
当我在写另一篇展示如何在文本分类目标中使用单词嵌入的文章时,我…
为了让计算机确定哪些文本是好的,哪些是坏的,我们需要对其进行标注。可以有任意数量的类,并且类本身可以表示非常多种多样的东西。让我们构建一些文本:
d = [
('This article is awesome', 1),
('There are just too much words here', 0),
('The math is actually wrong here', 0),
('I really enjoy learning new stuff', 1),
('I am kinda lazy so I just skim these texts', 0),
('Who cares about AI?', 0),
('I will surely be a better person after reading this!', 1),
('The author is pretty cute :)', 1)
]
我们有 8 个元组,其中第一个坐标是文本,第二个坐标是标签。标签 0 表示负面情绪,标签 1 表示正面情绪。为了建立一个功能模型,我们需要更多的数据(在我的实践中,如果只有两个类并且类是平衡的,那么一千或更多的标记数据点将开始给出好的结果)。
让我们做一些经典的文本预处理:
文本预处理功能
X_train = [x[0] for x in d] # Text
Y_train = [y[1] for y in d] # LabelX_train = [clean_text(x) for x in X_train]
清理后的文本( X_train ):
'this article is awesome'
'there are just too much words here'
'the math is actually wrong here'
'i really enjoy learning new stuff'
'i am kinda lazy so i just skim these texts'
'who cares about ai'
'i will surely be a better person after reading this'
'the author is pretty cute'
标签( Y_train ):
[1, 0, 0, 1, 0, 0, 1, 1]
既然我们在 X_train 矩阵和类别矩阵 Y_train 中有了预处理文本,我们需要为神经网络构建输入。
具有嵌入层的深度学习模型的输入使用嵌入矩阵。嵌入矩阵是行大小等于文档中唯一单词数量的矩阵,并且具有嵌入向量维数的列大小。因此,为了构建嵌入矩阵,需要创建单词嵌入向量或者使用预先训练的单词嵌入。在这个例子中,我们将读取一个虚构的单词嵌入文件并构建矩阵。
存储单词嵌入的常用格式是在一个文本文档中。
小型嵌入示例
我们姑且称上面的嵌入文件为 mini_embedding.txt 。要快速复制粘贴,请使用:
beautiful 1.5804182 0.25605154
boy -0.4558624 -1.5827272
can 0.9358587 -0.68037164
children -0.51683635 1.4153042
daughter 1.1436981 0.66987246
family -0.33289963 0.9955545
在这个例子中,嵌入维度等于 2,但是在来自链接https://nlp.stanford.edu/projects/glove/的单词嵌入中,维度是 300。在这两种情况下,结构都是单词是第一个元素,后面是由空格分隔的系数。当行尾有新的行分隔符时,坐标结束。
为了阅读这样的文本文档,让我们创建一个类:
读取嵌入并创建嵌入矩阵的类
让我们假设您在嵌入文件夹中有嵌入文件。
文件结构
embedding = Embeddings(
'embeddings/mini_embedding.txt',
vector_dimension=2
)
embedding_matrix = embedding.create_embedding_matrix()
我们尚未扫描任何文档,因此嵌入矩阵将返回 mini_embeddings.txt 文件中的所有单词:
array([[ 1.58041823, 0.25605154],
[-0.4558624 , -1.58272719],
[ 0.93585873, -0.68037164],
[-0.51683635, 1.41530418],
[ 1.1436981 , 0.66987246],
[-0.33289963, 0.99555451]])
嵌入矩阵的列数将始终等于嵌入维度的数量,行数将等于文档中唯一单词的数量或用户定义的行数。
除非您的机器中有大量的 RAM,否则通常建议您最大限度地使用用于构建嵌入矩阵的训练文档的所有唯一单词来创建嵌入矩阵。在手套嵌入文件中,有数百万个单词,其中大多数甚至在大多数文本文档中一次也没有出现。因此,不建议使用来自大型嵌入文件的所有唯一单词来创建嵌入矩阵。
深度学习模型中的预训练单词嵌入被放入矩阵中,并在输入层中用作权重。来自 Keras API 文档【https://keras.io/layers/embeddings/ :
keras.layers.Embedding(**input_dim**, **output_dim**,...)Turns positive integers (indexes) into dense vectors of fixed size. eg. [[4], [20]] -> [[0.25, 0.1], [0.6, -0.2]]**This layer can only be used as the first layer in a deep learning model.**
两个主要的输入参数是输入 _ 尺寸和输出 _ 尺寸。
input_dim 等于我们文本中唯一单词的总数(或者用户定义的特定数量的唯一单词)。
输出尺寸等于嵌入向量尺寸。
为了构建唯一的单词字典,我们将使用 Keras 库中的 Tokenizer() 方法。
from keras.preprocessing.text import Tokenizertokenizer = Tokenizer()
tokenizer.fit_on_texts(X_train)
提醒一下,我们预处理过的 X_train 是:
'this article is awesome'
'there are just too much words here'
'the math is actually wrong here'
'i really enjoy learning new stuff'
'i am kinda lazy so i just skim these texts'
'who cares about ai'
'i will surely be a better person after reading this'
'the author is pretty cute'
Tokenizer()方法创建唯一单词的内部字典,并为每个单词分配一个整数。 tokenizer.word_index 的输出:
{'i': 1,
'is': 2,
'this': 3,
'just': 4,
'here': 5,
'the': 6,
'article': 7,
'awesome': 8,
'there': 9,
'are': 10,
'too': 11,
'much': 12,
'words': 13,
'math': 14,
'actually': 15,
'wrong': 16,
'really': 17,
'enjoy': 18,
'learning': 19,
'new': 20,
'stuff': 21,
'am': 22,
'kinda': 23,
'lazy': 24,
'so': 25,
'skim': 26,
'these': 27,
'texts': 28,
'who': 29,
'cares': 30,
'about': 31,
'ai': 32,
'will': 33,
'surely': 34,
'be': 35,
'a': 36,
'better': 37,
'person': 38,
'after': 39,
'reading': 40,
'author': 41,
'pretty': 42,
'cute': 43}
在我们的 X_train 文本中有 43 个独特的单词。让我们将文本转换成索引列表:
tokenizer.texts_to_sequences(X_train)**[[3, 7, 2, 8],
[9, 10, 4, 11, 12, 13, 5],
[6, 14, 2, 15, 16, 5],
[1, 17, 18, 19, 20, 21],
[1, 22, 23, 24, 25, 1, 4, 26, 27, 28],
[29, 30, 31, 32],
[1, 33, 34, 35, 36, 37, 38, 39, 40, 3],
[6, 41, 2, 42, 43]]**
我们 X_train 矩阵中的第一句话‘这篇文章真棒’转换成一个【3,7,2,8】的列表。这些索引表示 tokenizer 创建的字典中的键值:
{...
**'is': 2,**
**'this': 3,**
...
**'article': 7,**
**'awesome': 8,**
...}
方法给了我们一个列表列表,其中每个条目都有不同的维度,并且是无结构的。任何机器学习模型都需要知道特征维度的数量,并且对于新观察值的训练和预测,该数量必须相同。为了将序列转换成用于深度学习训练的结构良好的矩阵,我们将使用来自 Keras 的pad _ sequences()方法:
import numpy as np
from keras.preprocessing.sequence import pad_sequences# Getting the biggest sentence
max_len = np.max([len(text.split()) for text in X_train])# Creating the padded matrices
X_train_NN = tokenizer.texts_to_sequences(X_train)
X_train_NN = pad_sequences(string_list, maxlen=max_len)
X_train_NN 对象如下所示:
array([[ 0, 0, 0, 0, 0, 0, 3, 7, 2, 8],
[ 0, 0, 0, 9, 10, 4, 11, 12, 13, 5],
[ 0, 0, 0, 0, 6, 14, 2, 15, 16, 5],
[ 0, 0, 0, 0, 1, 17, 18, 19, 20, 21],
[ 1, 22, 23, 24, 25, 1, 4, 26, 27, 28],
[ 0, 0, 0, 0, 0, 0, 29, 30, 31, 32],
[ 1, 33, 34, 35, 36, 37, 38, 39, 40, 3],
[ 0, 0, 0, 0, 0, 6, 41, 2, 42, 43]])
行数等于 X_train 元素数,列数等于最长的句子(等于 10 个单词)。列数通常由用户在阅读文档之前定义。这是因为当处理现实生活中的标记文本时,最长的文本可能非常长(数千字),这将导致训练神经网络时计算机内存的问题。
为了使用预处理文本为神经网络创建一个整洁的输入,我使用了我定义的类 TextToTensor :
将文本转换为结构化矩阵
张量是一个可以容纳 N 维数据的容器。向量可以在 1 维空间存储数据,矩阵可以在 2 维空间存储数据,张量可以在 n 维空间存储数据。有关张量的更多信息:
https://www.kdnuggets.com/2018/05/wtf-tensor.html
texttotenser的完整用法:
# Tokenizing the text
tokenizer = Tokenizer()
tokenizer.fit_on_texts(X_train)# Getting the longest sentence
max_len = np.max([len(text.split()) for text in X_train])# Converting to tensor
TextToTensor_instance = TextToTensor(
tokenizer=tokenizer,
max_len=max_len
)X_train_NN = TextToTensor_instance.string_to_tensor(X_train)
现在我们可以从文本中创建一个张量,我们可以开始使用 Keras API 中的嵌入层。
from keras.models import Sequential
from keras.layers import Embedding
model = Sequential()
model.add(Embedding(
input_dim=44,
output_dim=3,
input_length=max_len))
model.compile('rmsprop', 'mse')
output_array = model.predict(X_train_NN)[0]
注意,在嵌入层中,input_dim 等于 44,但是我们的文本只有 43 个唯一的单词。这是因为 Keras API 中的嵌入定义:
input_dim : int > 0。词汇的大小,即最大整数索引+ 1。
output_array 如下所示:
array([[-0.03353775, 0.01123261, 0.03025569],
[-0.03353775, 0.01123261, 0.03025569],
[-0.03353775, 0.01123261, 0.03025569],
[-0.03353775, 0.01123261, 0.03025569],
[-0.03353775, 0.01123261, 0.03025569],
[-0.03353775, 0.01123261, 0.03025569],
**[ 0.04183744, -0.00413301, 0.04792741],
[-0.00870543, -0.00829206, 0.02079277],
[ 0.02819189, -0.04957005, 0.03384084],
[ 0.0394035 , -0.02159669, 0.01720046]**], dtype=float32)
输入序列为(X_train_NN 的第一个元素):
array([0, 0, 0, 0, 0, 0, **3, 7, 2, 8**])
嵌入层自动给一个整数分配一个大小为 output_dim 的向量,在我们的例子中等于 3。我们不能控制内部计算,并且分配给每个整数索引的向量不具有这样的特征,即在语义上紧密相关的单词之间的距离小于那些具有不同语义的单词之间的距离。
为了解决这个问题,我们将使用斯坦福大学自然语言处理系的预训练单词嵌入(https://nlp.stanford.edu/projects/glove/)。为了创建嵌入矩阵,我们将使用先前定义的方法。
让我们假设 X_train 再次是预处理文本的列表。
embed_path = 'embeddings\\glove.840B.300d.txt'
embed_dim = 300# Tokenizing the text
tokenizer = Tokenizer()
tokenizer.fit_on_texts(X_train)# Creating the embedding matrix
embedding = Embeddings(embed_path, embed_dim)
embedding_matrix = embedding.create_embedding_matrix(tokenizer, len(tokenizer.word_counts))
而文档 glove.840B.300d.txt 有几十万个唯一字,最终嵌入矩阵的形状是 (44,300) 。这是因为我们想尽可能地节省内存,整个文档中唯一单词的数量等于 44 。保存文本文档中所有其他单词的坐标是一种浪费,因为我们不会在任何地方使用它们。
为了在深度学习模型中使用嵌入矩阵,我们需要将该矩阵作为嵌入层中的权重参数进行传递。
from keras.models import Sequential
from keras.layers import Embedding# Converting to tensor
TextToTensor_instance = TextToTensor(
tokenizer=tokenizer,
max_len=max_len
)
X_train_NN = TextToTensor_instance.string_to_tensor(X_train)model = Sequential()
model.add(Embedding(
input_dim=44,
output_dim=300,
input_length=max_len,
**weights=**[embedding_matrix]))
model.compile('rmsprop', 'mse')
output_array = model.predict(X_train_NN)[0]
output_array 的形状现在是 (10,300) ,输出如下所示:
array([[ 0.18733 , 0.40595 , -0.51174 , ..., 0.16495 , 0.18757 ,
0.53874 ],
[ 0.18733 , 0.40595 , -0.51174 , ..., 0.16495 , 0.18757 ,
0.53874 ],
[ 0.18733 , 0.40595 , -0.51174 , ..., 0.16495 , 0.18757 ,
0.53874 ],
...,
[-0.34338 , 0.1677 , -0.1448 , ..., 0.095014, -0.073342,
0.47798 ],
[-0.087595, 0.35502 , 0.063868, ..., 0.03446 , -0.15027 ,
0.40673 ],
[ 0.16718 , 0.30593 , -0.13682 , ..., -0.035268, 0.1281 ,
0.023683]], dtype=float32)
Sheri Hooley 在 Unsplash 上拍摄的照片
到目前为止,我们已经介绍了:
- 什么是单词嵌入
- 从文本中创建张量
- 创建单词嵌入矩阵
- 什么是 Keras 嵌入层
- 如何利用嵌入矩阵
现在让我们把所有的事情放在一起,处理一个现实生活中的问题,决定一条来自 Twitter 的推文是否是关于自然灾害的。
# Importing generic python packages
import pandas as pd# Reading the data
train = pd.read_csv('data/train.csv')[['text', 'target']]
test = pd.read_csv('data/test.csv')# Creating the input for the pipeline
X_train = train['text'].tolist()
Y_train = train['target'].tolist()X_test = test['text'].tolist()
列车数据的形状是 (7613,2),的意思是,有个 7613 条 tweets 可以处理。让我们检查一下推文的分布情况:
train.groupby(['target'], as_index=False).count()
推文的分发
正如我们所看到的,至少对于真实世界的数据来说,这些类是平衡的。
“好”推文示例:
[
'Our Deeds are the Reason of this #earthquake May ALLAH Forgive us all',
'Forest fire near La Ronge Sask. Canada',
"All residents asked to 'shelter in place' are being notified by officers. No other evacuation or shelter in place orders are expected",
'13,000 people receive #wildfires evacuation orders in California ',
'Just got sent this photo from Ruby #Alaska as smoke from #wildfires pours into a school'
]
糟糕推文的一个例子:
[
"What's up man?",
'I love fruits',
'Summer is lovely',
'My car is so fast',
'What a goooooooaaaaaal!!!!!!'
]
让我们做一些文本预处理,看看上面的单词:
# Counting the number of words
from collections import Counter# Plotting functions
import matplotlib.pyplot as pltX_train = [clean_text(text) for text in X_train]
Y_train = np.asarray(Y_train)# Tokenizing the text
tokenizer = Tokenizer()
tokenizer.fit_on_texts(X_train)# Getting the most frequent wordsd1 = train.loc[train['target']==1, 'text'].tolist()
d0 = train.loc[train['target']==0, 'text'].tolist()d1 = [clean_text(x, stop_words=stop_words) for x in d1]
d0 = [clean_text(x, stop_words=stop_words) for x in d0]d1_text = ' '.join(d1).split()
d0_text = ' '.join(d0).split()topd1 = Counter(d1_text)
topd0 = Counter(d0_text)topd1 = topd1.most_common(20)
topd0 = topd0.most_common(20)plt.bar(range(len(topd1)), [val[1] for val in topd1], align='center')
plt.xticks(range(len(topd1)), [val[0] for val in topd1])
plt.xticks(rotation=70)
plt.title('Disaster tweets')
plt.show()plt.bar(range(len(topd0)), [val[1] for val in topd0], align='center')
plt.xticks(range(len(topd0)), [val[0] for val in topd0])
plt.xticks(rotation=70)
plt.title('Not disaster tweets')
plt.show()
灾难推文中的热门词汇
非灾难推文中的热门词汇
非灾难词汇比灾难词汇更通用。人们可能期望手套嵌入和深度学习模型能够捕捉到这些差异。
每条推文的字数分布:
字数分布
根据上面的分布,我们可以说,创建列大小为 20 的输入张量只会排除 tweets 中非常少量的单词。在 pro 方面,我们将赢得大量的计算时间。
深度学习模型架构如下:
本文中提到的包装所有东西的管道也被定义为 python 中的一个类:
完整的代码和整个工作管道可以在这里找到:
[## eligijus 112/Twitter-正版-推文
这个项目可以作为一个模板来做任何 NLP 任务。在这个项目中的情绪是一个字符串是否认为…
github.com](https://github.com/Eligijus112/twitter-genuine-tweets)
若要训练模型,请使用以下代码:
results = Pipeline(
X_train=X_train,
Y_train=Y_train,
embed_path='embeddings\\glove.840B.300d.txt',
embed_dim=300,
stop_words=stop_words,
X_test=X_test,
max_len=20,
epochs=10,
batch_size=256
)
现在让我们创建两个文本:
好 =【“维尔纽斯失火!消防队在哪里???#紧急情况"]
不好 =【“寿司还是披萨?生活是艰难的:(("]
TextToTensor_instance = TextToTensor(
tokenizer=results.tokenizer,
max_len=20
)# Converting to tensors
good_nn = TextToTensor_instance.string_to_tensor(good)
bad_nn = TextToTensor_instance.string_to_tensor(bad)# Forecasting
p_good = results.model.predict(**good_nn**)[0][0]
p_bad = results.model.predict(**bad_nn**)[0][0]
p_bad = 0.014 和 p_good = 0.963 。这些概率是针对一条推文是否是关于一场灾难的问题。所以关于寿司的推文得分很低,关于火灾的推文得分很高。这意味着本文中提出的逻辑至少对虚构的句子有效。
在这篇文章中,我有:
- 展示了在监督问题中使用文本数据的逻辑和整体工作流程。
- 分享了 Python 中的完整工作代码,该代码采用原始输入,将其转换为矩阵,并使用 Tensorflow 训练深度学习模型
- 解读结果。
本文给出的代码可以应用于任何文本分类问题。编码快乐!
[1]推特灾难数据集
网址:https://www . ka ggle . com/competitions/NLP-getting-started/data执照:【https://opensource.org/licenses/Apache-2.0】T2
基于词语相似度的文本分类
尼克·舒利亚欣在 Unsplash 上的照片
NLP / NLTK Wordnet /文本分类
利用数据科学发现 FCA 的弱势客户
背景
英国金融服务监管机构 FCA 在其 2020/21 商业计划中关注的一个关键领域是公平对待银行客户。继去年夏天的初步磋商后,他们最近更新了指南,以更好地阐明漏洞的驱动因素:
我们已经确定了 4 个可能增加漏洞风险的关键驱动因素。它们是:
- 健康-影响执行日常任务能力的残疾或疾病。
- 生活事件 —重大生活事件,如丧亲、失业或关系破裂
- 复原力 —承受财务或情感冲击的能力低
- 能力 —对财务知识或理财信心(财务能力)低,以及其他相关领域的能力低,如读写能力或数字技能
随着与 COVID 相关的疾病和失业为这些驱动因素的蓬勃发展创造了肥沃的土壤,我想看看数据科学家如何帮助银行识别此类漏洞,以便他们能够更主动地提供支持服务。
我想象了一份客户和银行之间的电话对话,或者与聊天机器人或客户服务代理的打字对话。并期待 NLP 技术来帮助自动检测这些驱动程序。
潜在策略:
1。单词相似度:扫描文本段落中的关键词(例如休假)或它们的同义词。
22。从标记的例子中导出 n-gram 特征,并使用该模型对未来的文本进行分类。
鉴于缺乏训练数据,我们采用策略 1。
履行
1.规范化漏洞驱动因素
首先,我们将漏洞的 4 个驱动因素分解成一个更长的不同类别的列表,然后我们可以在文本中明确地搜索:
1。死亡,
2。冗余,
3。疾病,
等…
2.为 NLTK Wordnet 翻译类别
由于我们将使用 NLTK 中可用的相似性评分器,我们需要将这些类别翻译成 Wordnet (NLTK 的词汇数据库/字典)中描述的正确定义。为此,我们可以使用一个简单的函数,给出该单词的同义词列表及其定义:
word_scorer
哪个 forword _ scorer(‘冗余’)会输出:
[('redundancy.n.01',
'repetition of messages to reduce the probability of errors in transmission'),
('redundancy.n.02', 'the attribute of being superfluous and unneeded'),
('redundancy.n.03',
'(electronics) a system design that duplicates components to provide alternatives in case one component fails'),
('redundancy.n.04', 'repetition of an act needlessly')]
并因此帮助我们创建我们将搜索的主题字典(这里的关键字是用户友好的主题,值是 Wordnet 友好的表示):
主题 _ 词典
3.标记段落
这里有一段话我们可能想分析一下(显然是关于冗余的):
我在一个县议会的成人学习部门工作,我的工作由政府拨款支付。当资金用完的时候,财政年度已经过了一半,突然没钱给我发工资了。我被叫去和其他四个人(他们的工作也依赖于资金)开会,这个消息告诉了我们。在裁员最终确定之前,将有 12 周的咨询期。我们可以选择拿着遣散费走人,或者我们可以在议会的另一个部门找份工作。如果我们拒绝提供给我们的工作,我们可能会失去裁员协议。
我们将对其进行标记,这样我们就可以遍历单词标记列表
删除停用词并标记文本
4.根据单个主题给文章打分
我们将按照以下步骤实现一个函数:
- 遍历文章中的单词标记,将每个单词输入 wordnet.synsets()以列出单词的所有可能用法(例如,参见上面列出的单词“redundancy”的可能用法)
- 遍历单词的同义词集,并比较它们与感兴趣的主题的高度相似性(我们将使用 wup_similarity )。
- 如果这个词的任何可能用法与主题有很高的相似度(大于我们定义的相似度阈值),我们说这个主题已经被提及,然后我们继续下一个词。
- 我们还可以添加 return_hits 选项,这将使我们能够报告哪些单词触发了相似性阈值。
5.对照整个主题词典给文章打分
然后,我们可以重用函数来遍历主题字典中的每个主题:
因此计算该段落与每个主题相似度,并返回被认为相似的单词。
将这些函数打包到一个名为 synonym.py 的模块中后,运行最后一个函数就很简单了:
运行 multi_topic_scorer
它输出下面的结果字典,我们可以看到冗余的主题在文章中被提到了 3 次,每次都是因为提到了单词“redundancy ”,尽管它可能是任何其他类似的单词或相似的单词,如果被使用的话,可能会增加分数:
{'disability': (0, []),
'death': (0, []),
'health problems': (0, []),
'being a carer': (0, []),
'living alone': (0, []),
'job loss (fired)': (0, []),
'job loss (redundancy)': (3, ['redundancy', 'redundancy', 'redundancy']),
'job loss (furlough)': (0, [])}
6.检查结果
我们还可以使用我们之前提到的 word_scorer 来比较上面列出的单词(在这种情况下,它只是“冗余”)与主题的 wordnet 定义(即。redundancy . n . 02′),以查看“冗余”一词的哪种用法被认为是相似的
带有相似性得分的 word_scorer
[('redundancy.n.01',
'repetition of messages to reduce the probability of errors in transmission',
0.25),
('redundancy.n.02', 'the attribute of being superfluous and unneeded', 1.0),
('redundancy.n.03',
'(electronics) a system design that duplicates components to provide alternatives in case one component fails',
0.2222222222222222),
('redundancy.n.04', 'repetition of an act needlessly', 0.2222222222222222)]
返回的相似性分数指向第二个条目(本身),所以在这种情况下,这是一个多余的测试,但是在一个单词被列为不期望的相似的情况下,运行这个测试可以发现该单词的哪个用法被确切地认为是相似的。
结论
- 我们已经创建了一个简单的脚本,它能够根据预先定义的感兴趣的主题对一段文本进行分类(在本例中:银行的脆弱客户)。
- 这种方法不需要带标签的训练数据,因为我们正在计算预先计算的词向量之间的相似性得分。
- 这种方法还能够执行多标签文本分类,其中一段文本(甚至该文本中的每个单词)可以潜在地分配给多个主题,如果它们足够相似的话。这对于识别具有多个漏洞的客户非常重要。
总而言之:简单、优雅、灵活的解决方案!
GitHub 回购包含在下面,欢迎任何反馈。
根据 FCA 指南 4 漏洞的关键驱动因素:健康状况,NLP 如何用于识别易受攻击的客户
github.com](https://github.com/noahberhe/Vulnerable-Customers)
…感谢您的阅读!
PyTorch 中基于 CNN 的文本分类
使用 PyTorch 中实现的 CNN 构建文本分类器的分步指南。
谢尔比·米勒在 Unsplash 上的照片
“深度学习不仅仅是添加层”
这个博客的目的是通过实现卷积神经网络来开发一个逐步的文本分类器。所以,这个博客分为以下几个部分:
- 简介
- 预处理
- 车型
- 培训
- 评估
所以,让我们开始吧!
介绍
可以从不同的方法来解决文本分类问题,例如,考虑单词在给定文本中相对于这些单词在完整语料库中的出现的出现频率。
另一方面,存在将文本建模为单词或字符序列的其他方法,这种类型的方法主要利用基于递归神经网络架构的模型。
如果你想了解更多关于 LSTM 递归神经网络的文本分类,看看这个博客:py torch 中 LSTMs 的文本分类
然而,还有另一种方法,其中文本被建模为给定空间中单词的分布。这是通过使用卷积神经网络(CNN)实现的。
因此,我们将从最后提到的方法开始,我们将使用基于 CNN 的架构,建立一个模型来分类文本,考虑组成文本的一组单词在空间中的分布。
开始吧!
预处理
本模型所用数据取自 Kaggle 大赛: 真实与否?NLP 与灾难推文
数据集的第一行如图 1 所示:
图一。数据集头|按作者分类的图像
我们可以看到,需要创建一个预处理管道来加载文本,清理它,标记它,填充它和分割成训练和测试组。
载入文字。由于我们要处理的文本已经在我们的存储库中,我们只需要在本地调用它并删除一些无用的列。
代码 1。加载数据功能
干净的文字。在这种情况下,我们需要从文本中删除特殊符号和数字。我们将只使用小写单词。
代码 2。清除文本功能
词语虚化。对于标记化,我们将使用来自 nltk 库的 word_tokenize 函数(一种非常简单的句子标记化方法)。在此之后,我们将需要生成一个字典,其中包含数据集中最常用的单词 x (这是为了降低问题的复杂性)。因此,正如您在代码 3 的第 3 行中看到的,应用了标记化。在第 14 行中,选择了最常见的" x "单词,在第 16 行中构建了单词字典(正如您所看到的,字典从索引 1 开始,这是因为我们保留了索引 0 来应用填充)。
代码 3。标记化和构建词汇功能
到目前为止,每条 tweet 都已经标记化了,但是我们需要将每个单词标记转换成数字格式,因此我们将使用代码 3 中生成的字典将每个单词转换成基于索引的表示形式。
代码 4。从 word 到 idx 函数
填充。正如你所想象的,不是所有的 tweetss 都一样长,但是每条 tweet 都有相同的字数是很重要的。这就是我们引入填充的原因。填充是为了标准化每条 tweet 的长度。在这种情况下,我们将用于填充的值将是数字零(我们在构建词汇词典时保留的索引)。
代码 5。填充功能
分割介绍训练和测试。这个预处理管道的最后一步是将数据分为训练和测试。为此,我们将使用 scikit learn 提供的功能。
代码 6。分割数据功能
完整的预处理类如下所示:
代码 7。预处理类
很好,到目前为止,我们已经完成了所有的预处理,我们已经有了我们的训练和测试集,是时候看看模型了!
模型
实现的模型将使用 n 元单词,即不同的核大小将应用于同一个句子(指基于 n 元单词的作文)。然后,使用 max pooling 函数减少这些内核的每个输出。最后,这些输出中的每一个将被连接在单张量中,以被引入线性层,该线性层将被激活函数过滤以获得最终结果。
图二。建筑模型|作者图片
这里有完整的实现:https://github . com/FernandoLpz/Text-Classification-CNN-py torch
正如我们所见,卷积层没有堆叠。每个卷积层都由一个特定的核大小定义,这个大小就是所讨论的“n 元文法”的定义。此外,每个卷积的每个输出都使用最大池减少。最后,每个张量被连接起来形成一个单一的张量,它将被引入到线性层。
现在让我们看看如何使用 PyTorch 框架在代码中完成所有这些工作。首先,我们需要创建神经网络的构造器,为此,我们将定义一些重要的参数以及卷积、最大池和线性层。
代码 8。文本分类器构造器
从第 16 行到第 19 行,我们为每个卷积定义了个不同的内核(记住在这种情况下,内核大小充当了 n-gram 的大小)。
第 22 行指的是对每层卷积的输出通道数量的定义。第 24 行是指滑动窗口(内核)时要考虑的跳转次数。
在第 27 行定义了嵌入层。我们可以看到输入单词的数量(词汇量的大小),包含了一个" +1 "这是因为我们考虑的索引是指填充,在这种情况下它是索引 0。
从第 30 行到第 33 行,我们定义了每个卷积层。同样,从第 36 行到第 39 行,我们定义了每个最大池层。
最后在第 42 行,我们定义了线性层。值得注意的是,该层的输入元素的数量由一个函数定义。这是因为通过应用卷积和最大池(在不同的内核大小下),修改了输出张量的大小。同样,这样的输出张量将被连接并简化为一维张量,(展平)。这就是为什么我们实现了一个函数来计算线性层的输入大小。
每个卷积和最大汇集层的输出张量的大小由以下函数定义:
图 3。输出矢量大小| 来源
因此,计算线性图层输入大小的函数由以下公式确定:
代码 9。计算线性图层的输入大小
到目前为止,我们已经定义了构造函数。是时候继续前进了,让我们开始吧!
forward 函数将获取分词的向量,并通过嵌入层。随后,每个嵌入语句将通过卷积和最大池层的每一层,最后,得到的向量将被串接和归约以引入到线性层。
代码 10。正向功能
在第 4 行,输入向量通过嵌入层。在第 7、12、17 和 22 行中,卷积运算(具有不同的内核大小)被应用于嵌入序列。在第 27 行上,每个输出向量被连接,在第 28 行上,该向量被减少到一维(展平)。随后,展平向量通过一个线性层,其激活函数为 sigmoid 。
所以完整的架构看起来会是这样的:
图 3。文本分类模型|按作者分类的图片
太好了,我们已经看到了如何定义神经网络的架构以及前向函数,是时候看看训练函数了,让我们开始吧!
培训
正如我们在预处理阶段看到的,训练和测试数据已经准备好实现了。然而,我们需要将它们转换成基于 torch 的数据类型,并创建批处理生成器。为此,我们将利用 PyTorch 框架提供的数据集和数据加载器模块,实现如下:
代码 11。数据处理程序类
为了使用该类,我们只需要实例化然后初始化数据加载器。
代码 12。数据生成程序
对于训练阶段,我们必须定义数据加载器(我们在上一步中已经完成了)并定义优化器(在本例中,我们使用的是 RMSprop 优化器)。一旦一切准备就绪,我们就可以开始训练循环了。
代码 13。培训阶段
在第 15 行,我们对每个时期进行迭代。在第 20 行,我们使用数据加载器对每个批次进行迭代。在第 17 行模型被设置为训练模式(这意味着梯度将被更新)。在第 25 行,模型被输入。在第 28 行计算了误差。在第 29 行,存放梯度的变量被清除。在第 34 行计算梯度。在第 37 行,参数被更新。
最后,保存预测,并调用评估函数来获得训练和测试数据的准确性。所以,让我们在下一节看到这一点!
估价
太好了,我们终于进入评估环节了。让我们看看这是怎么回事。
这次我们将使用准确性作为衡量模型性能的标准。虽然有预定义的函数可以直接计算,但这次我们将手动计算,即计算真阳性以及假阳性。
代码 14。评价函数
在这种情况下,我们将 0.5 定义为阈值,以确定结果类的值是正还是负。
恭喜你!我们已经到了这个教程博客的结尾。该准则欢迎任何建议和/或评论。随意分叉或克隆!
这里有完整的实现:https://github . com/FernandoLpz/Text-class ification-CNN-py torch
结论
在这篇教程博客中,我们学习了如何使用基于卷积的神经网络架构实现 PyTorch 框架来生成文本分类模型。
TensorFlow 2(无泪)中拥抱人脸变形器的文本分类
拥抱脸变形器包是一个非常流行的 Python 库,提供了对各种自然语言处理(NLP)任务非常有用的预训练模型。它以前只支持 PyTorch ,但截至 2019 年末, TensorFlow 2 也支持。虽然该库可以用于许多任务,从自然语言推理(NLI)到问题回答,文本分类仍然是最流行和最实用的用例之一。
ktrain 库是 TensorFlow 2 中tf.keras
的轻量级包装器。它旨在使深度学习和人工智能更容易被初学者和领域专家所应用。从 0.8 版本开始, ktrain 现在包含了一个简化界面,可以对人脸变形金刚进行文本分类。在本文中,我们将向您展示如何使用拥抱面部变形器只用几行代码就可以构建、训练和部署一个文本分类模型。
本文的源代码有两种形式:
- 这款谷歌 Colab 笔记本或者
- 一个教程笔记本在 ktrain 的 GitHub 仓库里
入门指南
让我们从安装 ktrain 开始。在确保 TensorFlow 2 安装在您的系统上之后,您可以安装 ktrain 与:
pip3 install ktrain
在本文中,我们将使用20 个新闻组数据集构建一个包含四个新闻组类别的小型训练集。目标是建立一个模型,可以预测给定文章的新闻组类别。这将为我们提供一个在相对较小的训练集上观看变形金刚的机会,这是迁移学习的强大优势之一。顺便提一下,这是 scikit-learn 使用文本数据教程中使用的相同数据集。让我们使用 scikit-learn 获取20 个新闻组数据集,并将所有内容加载到数组中进行训练和验证:
接下来,我们必须从拥抱脸中选择一个预训练的模型,这些模型都在这里的中列出。在撰写本文时,变压器库支持 TensorFlow 2 的以下预训练模型:
- BERT:BERT-base-不区分大小写,BERT-large-不区分大小写,BERT-base-多语言-不区分大小写,以及其他。
- distil Bert:distil Bert-base-不区分大小写,distil Bert-base-多语言区分大小写,distil Bert-base-德语区分大小写,以及其他
- 艾伯特: 艾伯特-基-v2,艾伯特-大-v2,其他
- 罗伯塔基地,罗伯塔大,罗伯塔大
- XLM:xlm-MLM-xnli 15–1024,xlm-MLM-100–1280,其他
- XLNet:XLNet-base-cased,xlnet-large-cased
处理问题
实际上,由于拥抱脸变形金刚库中的 TensorFlow 支持相对较新,在撰写本文时,上面列出的几个模型会产生错误。例子包括:
例如,从 变形金刚库 的 v2.3.0 开始,distil-bert-uncased
模型工作得很好,但是distilbert-base-multilingual-cased
模型在训练期间抛出异常。
拥抱脸团队正在努力解决这样的问题。一些问题已经合并但未发布决议。例如, transformers-v2.3.0 中与 XLNet 相关的问题可以通过简单地将modeling_tf_xlnet.py
中的第 555 行从 transformers 库中替换为:
input_mask = 1.0 - tf.cast(attention_mask, dtype=dtype_float)
如本 PR 中所述。如果遇到某个特定模型的问题,你可以尝试在变形金刚 GitHub 库上搜索问题,寻找一个以变形金刚库补丁形式的解决方案。
选择蒸馏模型
与此同时,出于本教程的目的,我们将展示一个流行且极其有用的模型,该模型已经过验证,可以在 transformers 库的 v2.3.0 中工作(撰写本文时的当前版本)。BERT 模型通过在 11 个不同的自然语言处理任务中实现最先进的性能,代表了 2018-2019 年人工智能的重大突破之一。不幸的是,BERT 也是一个非常大且需要大量内存的模型,无论是训练还是推理都很慢。因此,BERT 不太适合生产环境。distill BERT 是 BERT 的“蒸馏”版本,更小更快,同时保留了 BERT 的大部分准确性。出于这些原因,我们将在本教程中使用一个未封装的英语模型:
接下来,我们将使用 ktrain 轻松快速地构建、训练、检查和评估模型。
步骤 1:创建一个转换器实例
ktrain 中的Transformer
类是围绕拥抱脸变形金刚库的简单抽象。让我们通过提供模型名称、序列长度(即maxlen
参数)并用目标名称列表填充classes
参数来实例化一个。
请注意,基于 BERT 的模型的最大序列长度通常为 512。小于maxlen
标记的文档将被填充,大于maxlen
标记的文档将被截断。
步骤 2:预处理数据集
然后,我们将训练和验证数据集预处理成所选预训练模型(在本例中为 DistilBERT)预期的格式。
步骤 3:创建一个模型并包含学员
接下来,我们定义一个带有预训练权重的分类器,并随机初始化可以微调的最终层。该模型将被包装在一个 ktrain Learner
对象中,这将允许我们轻松地训练和检查该模型,并使用它对新数据进行预测。
如果您在训练期间遇到内存不足错误,您可以尝试降低上面的batch_size
或降低步骤 1 中的maxlen
参数。
步骤 4[可选]:估计学习率
我们将使用 ktrain 中的学习率查找器来为我们的模型和数据集估计一个好的学习率。
对于基于 BERT 的模型, 2e-5 和 5e-5 之间的学习率通常在大范围的数据集上运行良好。因此,这一步是可选的。
这里,我们将选择与亏损下降相关的最高学习率。让我们选择 5 e-5 作为学习率。有趣的是,learning rate finder 的估计值(如图所示)与 Google 报告的通常对 BERT 和其他 transformer 模型最有效的学习率范围一致。
第五步:训练模型
对于培训,我们将调用 ktrain 中的fit_onecycle
方法,该方法采用了由 Leslie Smith 提出的1 周期策略。一般来说,学习率计划具有增加学习率的初始预热期,然后是逐渐降低学习率的衰减期,这对于基于变压器的模型来说往往工作得很好。
经过四个时期后,我们的验证准确率为 96.27%,,这比 SVM 在 scikit-learn 关于文本分类的教程中达到的 91%的准确率好了不少。
步骤 6:检查模型
让我们调用view_top_losses
方法来检查我们的模型最容易出错的新闻组帖子。
如您所见,在验证集中与 ID 1355 相关联的 newgroup 帖子位于comp.graphics
新闻组(即计算机图形学),但主要是关于色盲,这是一个医学话题。因此,我们的模型对这篇文章的sci.med
的预测是可以理解和原谅的。
第七步:对新数据进行预测
我们可以实例化一个Predictor
对象来轻松预测新的例子。
predictor
也可用于通过 eli5 和 lime 库解释特定示例的分类来进一步检查模型;
用绿色突出显示的单词似乎导致我们的模型将这个例子放在soc.religion.christian
类别中。正如你所看到的,突出显示的单词都符合这一类别的直觉。
注意,为了让explain
工作,您需要首先安装一个分叉版本的 eli5 库,该库被修改为支持 TensorFlow Keras:
pip3 install git+https://github.com/amaiya/eli5@tfkeras_0_10_1
最后,predictor
对象可以保存到磁盘上,并在以后的实际部署场景中重新加载:
默认情况下,predict
返回预测的类标签,但是predict_proba
将返回每个类的预测概率,如上所示。
结论
ktrain 可以轻松试验不同的变压器型号。例如,传统的 BERT 模型可以很容易地进行训练,以便与上面训练的 DistilBERT 模型进行比较,只需替换如下的MODEL_NAME
:
import ktrain
from ktrain import text
**MODEL_NAME = 'bert-base-uncased'**
t = text.Transformer(MODEL_NAME, maxlen=500,
classes=train_b.target_names)
trn = t.preprocess_train(x_train, y_train)
val = t.preprocess_test(x_test, y_test)
model = t.get_classifier()
learner = ktrain.get_learner(model, train_data=trn, val_data=val, batch_size=6)
learner.fit_onecycle(3e-5, 1)
不管选择什么样的MODEL_NAME
,包装在Learner
对象中的底层模型只是另一个 TensorFlow Keras 模型:
因此,一旦经过训练,如果愿意,可以直接使用 TensorFlow 和/或 transformers 库本身来管理它。在你的下一个文本分类项目中,请随意尝试一下 ktrain。
更多关于 ktrain 的信息,请访问我们的 GitHub 库。
基于自然语言处理的文本分类:Tf-Idf vs Word2Vec vs BERT
预处理、模型设计、评估、词袋的可解释性、词嵌入、语言模型
摘要
在本文中,我将使用 NLP 和 Python 解释文本多类分类的 3 种不同策略:老式的单词袋(使用 Tf-Idf ) ,著名的单词嵌入(使用 Word2Vec),以及尖端的语言模型(使用 BERT)。
【NLP(自然语言处理) 是人工智能领域,研究计算机与人类语言之间的交互,特别是如何给计算机编程,以处理和分析大量自然语言数据。NLP 通常用于文本数据的分类。文本分类就是根据文本数据的内容给文本数据分配类别的问题。
有不同的技术可以从原始文本数据中提取信息,并使用它来训练分类模型。本教程比较了老派的单词袋(与简单的机器学习算法一起使用)、流行的单词嵌入模型(与深度学习神经网络一起使用)和最先进的语言模型(与基于注意力的变形金刚的转移学习一起使用),这些模型已经彻底改变了 NLP 的前景。
我将展示一些有用的 Python 代码,这些代码可以很容易地应用于其他类似的情况(只需复制、粘贴、运行),并通过注释遍历每一行代码,以便您可以复制这个示例(下面是完整代码的链接)。
[## mdipietro 09/data science _ 人工智能 _ 实用工具
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
我将使用“新闻类别数据集”,其中为您提供了从赫芬顿邮报获得的 2012 年至 2018 年的新闻标题,并要求您将它们分类到正确的类别,因此这是一个多类别分类问题(下面的链接)。
根据标题和简短描述识别新闻的类型
www.kaggle.com](https://www.kaggle.com/rmisra/news-category-dataset)
特别是,我将经历:
- 设置:导入包、读取数据、预处理、分区。
- 用 scikit-learn 进行特征工程&特征选择&机器学习,用 lime 进行测试&评估,可解释性。
- 单词嵌入:用 gensim 拟合一个 Word2Vec,用 tensorflow/keras 进行特征工程&深度学习,用注意力机制测试&评价,可解释性。
- 语言模型:用变形金刚进行特征工程,用变形金刚和tensor flow/keras测试&评估向预先训练好的 BERT 转移学习。
设置
首先,我需要导入以下库:
**## for data** import **json** import **pandas** as pd
import **numpy** as np**## for plotting**
import **matplotlib**.pyplot as plt
import **seaborn** as sns**## for processing** import **re**
import **nltk****## for bag-of-words**
from **sklearn** import feature_extraction, model_selection, naive_bayes, pipeline, manifold, preprocessing**## for explainer**
from **lime** import lime_text**## for word embedding**
import **gensim** import gensim.downloader as gensim_api**## for deep learning**
from **tensorflow**.keras import models, layers, preprocessing as kprocessing
from tensorflow.keras import backend as K**## for bert language model**
import **transformers**
数据集包含在一个 json 文件中,所以我将首先用 json 把它读入一个字典列表,然后把它转换成一个 pandas Dataframe。
lst_dics = []
with **open**('data.json', mode='r', errors='ignore') as json_file:
for dic in json_file:
lst_dics.append( json**.loads**(dic) )**## print the first one**
lst_dics[0]
原始数据集包含超过 30 个类别,但是出于本教程的目的,我将使用 3 个类别的子集:娱乐、政治和技术。
**## create dtf**
dtf = pd.DataFrame(lst_dics)**## filter categories**
dtf = dtf[ dtf["category"].isin(['**ENTERTAINMENT**','**POLITICS**','**TECH**']) ][["category","headline"]]**## rename columns**
dtf = dtf.rename(columns={"category":"**y**", "headline":"**text**"})**## print 5 random rows**
dtf.sample(5)
为了理解数据集的组成,我将通过用条形图显示标签频率来查看目标的单变量分布。
fig, ax = plt.subplots()
fig.suptitle(**"y"**, fontsize=12)
dtf[**"y"**].reset_index().groupby(**"y"**).count().sort_values(by=
"index").plot(kind="barh", legend=False,
ax=ax).grid(axis='x')
plt.show()
数据集是不平衡的:与其他相比,科技新闻的比例非常小,这将使模型识别科技新闻相当困难。
在解释和构建模型之前,我将给出一个预处理的例子,通过清理文本、删除停用词和应用词汇化。我将编写一个函数,并将其应用于整个数据集。
**'''
Preprocess a string.
:parameter
:param text: string - name of column containing text
:param lst_stopwords: list - list of stopwords to remove
:param flg_stemm: bool - whether stemming is to be applied
:param flg_lemm: bool - whether lemmitisation is to be applied
:return
cleaned text
'''**
def **utils_preprocess_text**(text, flg_stemm=False, flg_lemm=True, lst_stopwords=None):
**## clean (convert to lowercase and remove punctuations and
characters and then strip)**
text = re.sub(r'[^\w\s]', '', str(text).lower().strip())
**## Tokenize (convert from string to list)**
lst_text = text.split() **## remove Stopwords**
if lst_stopwords is not None:
lst_text = [word for word in lst_text if word not in
lst_stopwords]
**## Stemming (remove -ing, -ly, ...)**
if flg_stemm == True:
ps = nltk.stem.porter.PorterStemmer()
lst_text = [ps.stem(word) for word in lst_text]
**## Lemmatisation (convert the word into root word)**
if flg_lemm == True:
lem = nltk.stem.wordnet.WordNetLemmatizer()
lst_text = [lem.lemmatize(word) for word in lst_text]
**## back to string from list**
text = " ".join(lst_text)
return text
该函数从语料库中删除一组给定的单词。我可以用 nltk 为英语词汇创建一个通用停用词列表(我们可以通过添加或删除单词来编辑这个列表)。
lst_stopwords = **nltk**.corpus.stopwords.words("**english**")
lst_stopwords
现在,我将对整个数据集应用我编写的函数,并将结果存储在一个名为“ text_clean 的新列中,以便您可以选择处理原始语料库或预处理文本。
dtf["**text_clean**"] = dtf["text"].apply(lambda x:
**utils_preprocess_text**(x, flg_stemm=False, **flg_lemm=True**,
**lst_stopwords=lst_stopwords**))dtf.head()
如果你对更深入的文本分析和预处理感兴趣,可以查看这篇文章。考虑到这一点,我将把数据集划分为训练集(70%)和测试集(30%),以便评估模型的性能。
**## split dataset**
dtf_train, dtf_test = model_selection.**train_test_split**(dtf, test_size=0.3)**## get target**
y_train = dtf_train[**"y"**].values
y_test = dtf_test[**"y"**].values
我们开始吧,好吗?
词汇袋
单词袋 模型很简单:它从一个文档语料库中构建一个词汇表,并统计这些单词在每个文档中出现的次数。换句话说,词汇表中的每个单词都成为一个特征,一个文档由一个具有相同词汇表长度的向量(一个“单词包”)来表示。例如,让我们用这种方法来表达 3 个句子:
特征矩阵形状: 文档数量x***词汇长度*****
可以想象,这种方法会导致一个显著的维数问题:文档越多,词汇表就越大,因此特征矩阵将是一个巨大的稀疏矩阵。因此,在单词袋模型之前通常要进行重要的预处理(单词清理、停用词去除、词干化/词条化),目的是减少维数问题。
术语频率不一定是文本的最佳表示。事实上,你可以在语料库中找到出现频率最高但对目标变量几乎没有预测能力的常用词。为了解决这个问题,单词包有一个高级变体,它不是简单的计数,而是使用术语频率-逆文档频率(或 Tf-Idf)。基本上,一个词的值随着 count 成正比增加,但是和这个词在语料库中的出现频率成反比。
让我们从特征工程开始,通过从数据中提取信息来创建特征的过程。我将使用限制为 10,000 个单词的 Tf-Idf 矢量器(因此我的词汇长度将为 10k),捕获单字母词(即“ new ”和“ york ”)和双字母词(即“ new york ”)。我还将提供经典计数矢量器的代码:
*****## Count (classic BoW)***
*vectorizer = feature_extraction.text.****CountVectorizer****(max_features=10000,* ngram_range=(1,2))
***## Tf-Idf (advanced variant of BoW)***
vectorizer = feature_extraction.text.**TfidfVectorizer**(max_features=10000, ngram_range=(1,2))**
现在,我将在训练集的预处理语料库上使用矢量器来提取词汇并创建特征矩阵。
**corpus = dtf_train["**text_clean**"]vectorizer.fit(corpus)
X_train = vectorizer.transform(corpus)
dic_vocabulary = vectorizer.vocabulary_**
特征矩阵 X_train 具有 34,265(训练中的文档数量)x 10,000(词汇长度)的形状,并且它非常稀疏:
**sns.**heatmap**(X_train.todense()[:,np.random.randint(0,X.shape[1],100)]==0, vmin=0, vmax=1, cbar=False).set_title('Sparse Matrix Sample')**
来自特征矩阵的随机样本(黑色的非零值)
为了知道某个单词的位置,我们可以在词汇表中查找它:
**word = "new york"dic_vocabulary[word]**
如果这个单词存在于词汇表中,这个命令打印一个数字 N ,意味着矩阵的第 N 个特征就是这个单词。
为了删除一些列并降低矩阵维数,我们可以执行一些特征选择,即选择相关变量子集的过程。我将如下进行:
- 将每个类别视为二进制(例如,“技术”类别对于技术新闻为 1,对于其他类别为 0);
- 执行卡方检验以确定特征和(二元)目标是否独立;
- 仅保留卡方检验中具有特定 p 值的要素。
**y = dtf_train["**y**"]
X_names = vectorizer.get_feature_names()
p_value_limit = 0.95dtf_features = pd.DataFrame()
for cat in np.unique(y):
chi2, p = feature_selection.**chi2**(X_train, y==cat)
dtf_features = dtf_features.append(pd.DataFrame(
{"feature":X_names, "score":1-p, "y":cat}))
dtf_features = dtf_features.sort_values(["y","score"],
ascending=[True,False])
dtf_features = dtf_features[dtf_features["score"]>p_value_limit]X_names = dtf_features["feature"].unique().tolist()**
我通过保留统计上最相关的特征,将特征的数量从 10,000 个减少到 3,152 个。让我们打印一些:
**for cat in np.unique(y):
print("# {}:".format(cat))
print(" . selected features:",
len(dtf_features[dtf_features["y"]==cat]))
print(" . top features:", ",".join(
dtf_features[dtf_features["y"]==cat]["feature"].values[:10]))
print(" ")**
我们可以通过给定这组新单词作为输入,在语料库上重新装配矢量器。这将产生更小的特征矩阵和更短的词汇表。
**vectorizer = feature_extraction.text.**TfidfVectorizer**(vocabulary=X_names)vectorizer.fit(corpus)
X_train = vectorizer.transform(corpus)
dic_vocabulary = vectorizer.vocabulary_**
新的特征矩阵 X_train 具有 is 34,265(训练中的文档数量)x 3,152(给定词汇的长度)的形状。让我们看看矩阵是否不那么稀疏:
来自新特征矩阵的随机样本(黑色非零值)
是时候训练一个机器学习模型并测试它了。我推荐使用朴素贝叶斯算法:一种概率分类器,它利用了贝叶斯定理,这是一种基于可能相关的条件的先验知识使用概率进行预测的规则。该算法最适合这种大型数据集,因为它独立考虑每个特征,计算每个类别的概率,然后预测概率最高的类别。
**classifier = naive_bayes.**MultinomialNB**()**
我将在特征矩阵上训练这个分类器,然后在转换后的测试集上测试它。为此,我需要构建一个 scikit-learn 管道:一个转换列表和一个最终估计器的顺序应用。将 Tf-Idf 矢量器和朴素贝叶斯分类器放在一个管道中,使我们能够在一个步骤中转换和预测测试数据。
****## pipeline**
model = pipeline.**Pipeline**([("**vectorizer**", vectorizer),
("**classifier**", classifier)])**## train classifier** model["classifier"].fit(X_train, y_train)**## test** X_test = dtf_test["text_clean"].values
predicted = model.predict(X_test)
predicted_prob = model.predict_proba(X_test)**
我们现在可以评估单词袋模型的性能,我将使用以下指标:
- 准确性:模型预测正确的比例。
- 混淆矩阵:按类别细分正确和错误预测数量的汇总表。
- ROC:说明在不同阈值设置下真阳性率与假阳性率的图表。曲线下的面积(AUC)表示分类器将随机选择的阳性观察值排列为高于随机选择的阴性观察值的概率。
- Precision:相关实例在检索到的实例中所占的比例。
- Recall:实际检索到的相关实例总数的一部分。
**classes = np.unique(y_test)
y_test_array = pd.get_dummies(y_test, drop_first=False).values **## Accuracy, Precision, Recall**
accuracy = metrics.accuracy_score(y_test, predicted)
auc = metrics.roc_auc_score(y_test, predicted_prob,
multi_class="ovr")
print("Accuracy:", round(accuracy,2))
print("Auc:", round(auc,2))
print("Detail:")
print(metrics.classification_report(y_test, predicted))
**## Plot confusion matrix**
cm = metrics.confusion_matrix(y_test, predicted)
fig, ax = plt.subplots()
sns.heatmap(cm, annot=True, fmt='d', ax=ax, cmap=plt.cm.Blues,
cbar=False)
ax.set(xlabel="Pred", ylabel="True", xticklabels=classes,
yticklabels=classes, title="Confusion matrix")
plt.yticks(rotation=0) fig, ax = plt.subplots(nrows=1, ncols=2)
**## Plot roc**
for i in range(len(classes)):
fpr, tpr, thresholds = metrics.roc_curve(y_test_array[:,i],
predicted_prob[:,i])
ax[0].plot(fpr, tpr, lw=3,
label='{0} (area={1:0.2f})'.format(classes[i],
metrics.auc(fpr, tpr))
)
ax[0].plot([0,1], [0,1], color='navy', lw=3, linestyle='--')
ax[0].set(xlim=[-0.05,1.0], ylim=[0.0,1.05],
xlabel='False Positive Rate',
ylabel="True Positive Rate (Recall)",
title="Receiver operating characteristic")
ax[0].legend(loc="lower right")
ax[0].grid(True)
**## Plot precision-recall curve** for i in range(len(classes)):
precision, recall, thresholds = metrics.precision_recall_curve(
y_test_array[:,i], predicted_prob[:,i])
ax[1].plot(recall, precision, lw=3,
label='{0} (area={1:0.2f})'.format(classes[i],
metrics.auc(recall, precision))
)
ax[1].set(xlim=[0.0,1.05], ylim=[0.0,1.05], xlabel='Recall',
ylabel="Precision", title="Precision-Recall curve")
ax[1].legend(loc="best")
ax[1].grid(True)
plt.show()**
BoW 模型对测试集的正确率为 85%(准确率为 0.85),但很难识别科技新闻(只有 252 个预测正确)。
让我们试着理解为什么该模型将新闻归入某一类别,并评估这些预测的解释能力。lime 包可以帮助我们建立一个解释器。为了举例说明,我将从测试集中随机观察,看看模型预测了什么,为什么。
****## select observation** i = 0
txt_instance = dtf_test["**text**"].iloc[i]**## check true value and predicted value**
print("True:", y_test[i], "--> Pred:", predicted[i], "| Prob:", round(np.max(predicted_prob[i]),2))**## show explanation**
explainer = lime_text.**LimeTextExplainer**(class_names=
np.unique(y_train))
explained = explainer.explain_instance(txt_instance,
model.predict_proba, num_features=3)
explained.show_in_notebook(text=txt_instance, predict_proba=False)**
****
这是有道理的:单词“克林顿”和“共和党”为这个模型指出了正确的方向(政治新闻),即使单词“舞台”在娱乐新闻中更常见。
单词嵌入
单词嵌入 是将词汇中的单词映射到实数向量的特征学习技术的统称。这些向量是根据出现在另一个单词之前或之后的每个单词的概率分布来计算的。换句话说,相同上下文的单词通常一起出现在语料库中,因此它们在向量空间中也将是接近的。例如,让我们看一下上一个例子中的三个句子:
嵌入 2D 向量空间的词
在本教程中,我将使用这个家族的第一个模型:谷歌的word 2 vec(2013)。其他流行的单词嵌入模型还有斯坦福的GloVe(2014)*和脸书的fast text(2016)。***
Word2Vec 利用语料库中的每个唯一单词产生通常具有数百维的向量空间,使得语料库中共享共同上下文的单词在空间中彼此靠近。这可以使用两种不同的方法来完成:从单个单词开始预测其上下文( Skip-gram )或者从上下文开始预测单词(连续单词包)。
在 Python 中,你可以像这样从genism-data加载一个预先训练好的单词嵌入模型:**
****nlp = gensim_api.load("**word2vec-google-news-300"**)****
我将使用 gensim 在训练数据语料库上拟合我自己的 Word2Vec,而不是使用预先训练的模型。在拟合模型之前,需要将语料库转换成 n 元文法列表的列表。在这个特殊的例子中,我将尝试捕获单字母(" york ")、双字母(" new york ")和三字母(" new york city ")。
***corpus = dtf_train["**text_clean**"] **## create list of lists of unigrams**
lst_corpus = []
for string in corpus:
lst_words = string.split()
lst_grams = [" ".join(lst_words[i:i+1])
for i in range(0, len(lst_words), 1)]
lst_corpus.append(lst_grams) **## detect bigrams and trigrams**
bigrams_detector = gensim.models.phrases.**Phrases**(lst_corpus,
delimiter=" ".encode(), min_count=5, threshold=10)
bigrams_detector = gensim.models.phrases.**Phraser**(bigrams_detector)trigrams_detector = gensim.models.phrases.**Phrases**(bigrams_detector[lst_corpus],
delimiter=" ".encode(), min_count=5, threshold=10)
trigrams_detector = gensim.models.phrases.**Phraser**(trigrams_detector)***
安装 Word2Vec 时,需要指定:
- 单词向量的目标大小,我用 300;
- 窗口,或句子中当前单词和预测单词之间的最大距离,我将使用语料库中文本的平均长度;
- 训练算法,我将使用 skip-grams (sg=1 ),因为通常它有更好的结果。
*****## fit w2v**
nlp = gensim.models.word2vec.**Word2Vec**(lst_corpus, size=300,
window=8, min_count=1, sg=1, iter=30)***
我们有自己的嵌入模型,所以我们可以从语料库中选择任何单词,并将其转换为向量。
***word = "data"
nlp[word].shape***
我们甚至可以通过应用任何降维算法(即 TSNE )来使用它将一个单词及其上下文可视化到一个更小的维度空间(2D 或 3D)中。
***word = "data"
fig = plt.figure()**## word embedding**
tot_words = [word] + [tupla[0] for tupla in
nlp.most_similar(word, topn=20)]
X = nlp[tot_words]**## pca to reduce dimensionality from 300 to 3**
pca = manifold.**TSNE**(perplexity=40, n_components=3, init='pca')
X = pca.fit_transform(X)**## create dtf**
dtf_ = pd.DataFrame(X, index=tot_words, columns=["x","y","z"])
dtf_["input"] = 0
dtf_["input"].iloc[0:1] = 1**## plot 3d**
from mpl_toolkits.mplot3d import Axes3D
ax = fig.add_subplot(111, projection='3d')
ax.scatter(dtf_[dtf_["input"]==0]['x'],
dtf_[dtf_["input"]==0]['y'],
dtf_[dtf_["input"]==0]['z'], c="black")
ax.scatter(dtf_[dtf_["input"]==1]['x'],
dtf_[dtf_["input"]==1]['y'],
dtf_[dtf_["input"]==1]['z'], c="red")
ax.set(xlabel=None, ylabel=None, zlabel=None, xticklabels=[],
yticklabels=[], zticklabels=[])
for label, row in dtf_[["x","y","z"]].iterrows():
x, y, z = row
ax.text(x, y, z, s=label)***
这很酷,但是嵌入这个词怎么能用来预测新闻类别呢?嗯,单词向量可以在神经网络中用作权重。这是怎么回事:
- 首先,将语料库转换成单词 id 的填充序列,以获得特征矩阵。
- 然后,创建一个嵌入矩阵,使得 id 为 N 的单词的向量位于第N行。**
- 最后,构建一个具有嵌入层的神经网络,该嵌入层用相应的向量对序列中的每个单词进行加权。
让我们从特征工程开始,通过使用 tensorflow/keras 将给予 Word2Vec 的相同预处理语料库(n 元语法列表的列表)转换成序列列表:**
*****## tokenize text**
tokenizer = kprocessing.text.**Tokenizer**(lower=True, split=' ',
oov_token="NaN",
filters='!"#$%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n')
tokenizer.fit_on_texts(lst_corpus)
dic_vocabulary = tokenizer.word_index**## create sequence**
lst_text2seq= tokenizer.texts_to_sequences(lst_corpus)**## padding sequence**
X_train = kprocessing.sequence.**pad_sequences**(lst_text2seq,
maxlen=15, padding="post", truncating="post")***
特征矩阵 X_train 的形状为 34,265 x 15(序列数 X 序列最大长度)。让我们想象一下:
***sns.heatmap(X_train==0, vmin=0, vmax=1, cbar=False)
plt.show()***
特征矩阵(34,265 x 15)
语料库中的每个文本现在都是长度为 15 的 id 序列。例如,如果文本中有 10 个标记,那么序列由 10 个 id+5 个 0 组成,这是填充元素(而不在词汇表中的单词的 id 是 1)。让我们打印一个来自训练集的文本如何被转换成一个带有填充和词汇的序列。
***i = 0
**## list of text: ["I like this", ...]**
len_txt = len(dtf_train["text_clean"].iloc[i].split())
print("from: ", dtf_train["text_clean"].iloc[i], "| len:", len_txt)
**## sequence of token ids: [[1, 2, 3], ...]**
len_tokens = len(X_train[i])
print("to: ", X_train[i], "| len:", len(X_train[i]))
**## vocabulary: {"I":1, "like":2, "this":3, ...}**
print("check: ", dtf_train["text_clean"].iloc[i].split()[0],
" -- idx in vocabulary -->",
dic_vocabulary[dtf_train["text_clean"].iloc[i].split()[0]])
print("vocabulary: ", dict(list(dic_vocabulary.items())[0:5]), "... (padding element, 0)")***
在继续之前,不要忘记在测试集上做同样的特性工程:
***corpus = dtf_test["**text_clean**"] **## create list of n-grams**
lst_corpus = []
for string in corpus:
lst_words = string.split()
lst_grams = [" ".join(lst_words[i:i+1]) for i in range(0,
len(lst_words), 1)]
lst_corpus.append(lst_grams) **## detect common bigrams and trigrams using the fitted detectors**
lst_corpus = list(bigrams_detector[lst_corpus])
lst_corpus = list(trigrams_detector[lst_corpus]) **## text to sequence with the fitted tokenizer**
lst_text2seq = tokenizer.texts_to_sequences(lst_corpus) **## padding sequence**
X_test = kprocessing.sequence.**pad_sequences**(lst_text2seq, maxlen=15,
padding="post", truncating="post")***
x _ 测试(14,697 x 15)
我们已经得到了我们的 X_train 和 X_test ,现在我们需要创建嵌入的矩阵,它将被用作神经网络分类器中的权重矩阵。****
*****## start the matrix (length of vocabulary x vector size) with all 0s**
embeddings = np.zeros((len(dic_vocabulary)+1, 300))for word,idx in dic_vocabulary.items():
**## update the row with vector**
try:
embeddings[idx] = nlp[word]
**## if word not in model then skip and the row stays all 0s**
except:
pass***
该代码生成一个形状为 22,338 x 300 的矩阵(从语料库中提取的词汇长度 x 向量大小)。它可以通过单词 id 导航,可以从词汇表中获得。
***word = "data"print("dic[word]:", dic_vocabulary[word], "|idx")
print("embeddings[idx]:", embeddings[dic_vocabulary[word]].shape,
"|vector")***
终于到了搭建深度学习模型的时候了。我将使用我将构建和训练的神经网络的第一个嵌入层中的嵌入矩阵来对新闻进行分类。输入序列中的每个 id 将被用作访问嵌入矩阵的索引。这个嵌入层的输出将是一个 2D 矩阵,对于输入序列中的每个单词 id 有一个单词向量(序列长度×向量大小)。让我们用句子“我喜欢这篇文章”作为例子:**
我的神经网络结构如下:
- 如前所述,嵌入层将序列作为输入,将单词向量作为权重。
- 一个简单的注意层,不会影响预测,但它将捕获每个实例的权重,并允许我们构建一个好的解释器(它对于预测是不必要的,只是为了解释,所以你可以跳过它)。注意机制在本文 (2014)中提出,作为序列模型(即 LSTM)问题的解决方案,以理解长文本的哪些部分实际上是相关的。
- 两层双向 LSTM,在两个方向上对序列中的单词顺序进行建模。
- 两个最终的密集层将预测每个新闻类别的概率。
*****## code attention layer**
def **attention_layer**(inputs, neurons):
x = layers.**Permute**((2,1))(inputs)
x = layers.**Dense**(neurons, activation="softmax")(x)
x = layers.**Permute**((2,1), name="**attention**")(x)
x = layers.**multiply**([inputs, x])
return x **## input**
x_in = layers.**Input**(shape=(15,))**## embedding**
x = layers.**Embedding**(input_dim=embeddings.shape[0],
output_dim=embeddings.shape[1],
weights=[embeddings],
input_length=15, trainable=False)(x_in)**## apply attention**
x = attention_layer(x, neurons=15)**## 2 layers of bidirectional lstm**
x = layers.**Bidirectional**(layers.**LSTM**(units=15, dropout=0.2,
return_sequences=True))(x)
x = layers.**Bidirectional**(layers.**LSTM**(units=15, dropout=0.2))(x)**## final dense layers**
x = layers.**Dense**(64, activation='relu')(x)
y_out = layers.**Dense**(3, activation='softmax')(x)**## compile**
model = models.**Model**(x_in, y_out)
model.compile(loss='sparse_categorical_crossentropy',
optimizer='adam', metrics=['accuracy'])
model.summary()***
现在,我们可以训练模型,并在实际测试集上测试它之前,检查用于验证的训练集子集的性能。
*****## encode y**
dic_y_mapping = {n:label for n,label in
enumerate(np.unique(y_train))}
inverse_dic = {v:k for k,v in dic_y_mapping.items()}
y_train = np.array([inverse_dic[y] for y in y_train])**## train**
training = model.fit(x=X_train, y=y_train, batch_size=256,
epochs=10, shuffle=True, verbose=0,
validation_split=0.3)**## plot loss and accuracy**
metrics = [k for k in training.history.keys() if ("loss" not in k) and ("val" not in k)]
fig, ax = plt.subplots(nrows=1, ncols=2, sharey=True)ax[0].set(title="Training")
ax11 = ax[0].twinx()
ax[0].plot(training.history['loss'], color='black')
ax[0].set_xlabel('Epochs')
ax[0].set_ylabel('Loss', color='black')
for metric in metrics:
ax11.plot(training.history[metric], label=metric)
ax11.set_ylabel("Score", color='steelblue')
ax11.legend()ax[1].set(title="Validation")
ax22 = ax[1].twinx()
ax[1].plot(training.history['val_loss'], color='black')
ax[1].set_xlabel('Epochs')
ax[1].set_ylabel('Loss', color='black')
for metric in metrics:
ax22.plot(training.history['val_'+metric], label=metric)
ax22.set_ylabel("Score", color="steelblue")
plt.show()***
不错!在某些纪元中,精确度达到 0.89。为了完成单词嵌入模型的评估,让我们预测测试集并比较之前使用的相同度量(度量的代码与之前相同)。**
*****## test**
predicted_prob = model.predict(X_test)
predicted = [dic_y_mapping[np.argmax(pred)] for pred in
predicted_prob]***
该模型的表现与上一个模型一样好,事实上,它也很难对科技新闻进行分类。
但是这也是可以解释的吗?是的,它是!我在神经网络中放置了一个注意力层,以提取每个单词的权重,并了解这些权重对分类一个实例的贡献有多大。因此,我将尝试使用注意力权重来构建一个解释器(类似于上一节中看到的解释器):****
*****## select observation** i = 0
txt_instance = dtf_test["**text**"].iloc[i]**## check true value and predicted value**
print("True:", y_test[i], "--> Pred:", predicted[i], "| Prob:", round(np.max(predicted_prob[i]),2)) **## show explanation
### 1\. preprocess input** lst_corpus = []
for string in [re.sub(r'[^\w\s]','', txt_instance.lower().strip())]:
lst_words = string.split()
lst_grams = [" ".join(lst_words[i:i+1]) for i in range(0,
len(lst_words), 1)]
lst_corpus.append(lst_grams)
lst_corpus = list(bigrams_detector[lst_corpus])
lst_corpus = list(trigrams_detector[lst_corpus])
X_instance = kprocessing.sequence.pad_sequences(
tokenizer.texts_to_sequences(corpus), maxlen=15,
padding="post", truncating="post")**### 2\. get attention weights**
layer = [layer for layer in model.layers if "**attention**" in
layer.name][0]
func = K.function([model.input], [layer.output])
weights = func(X_instance)[0]
weights = np.mean(weights, axis=2).flatten()**### 3\. rescale weights, remove null vector, map word-weight**
weights = preprocessing.MinMaxScaler(feature_range=(0,1)).fit_transform(np.array(weights).reshape(-1,1)).reshape(-1)
weights = [weights[n] for n,idx in enumerate(X_instance[0]) if idx
!= 0]
dic_word_weigth = {word:weights[n] for n,word in
enumerate(lst_corpus[0]) if word in
tokenizer.word_index.keys()}**### 4\. barplot**
if len(dic_word_weigth) > 0:
dtf = pd.DataFrame.from_dict(dic_word_weigth, orient='index',
columns=["score"])
dtf.sort_values(by="score",
ascending=True).tail(3).plot(kind="barh",
legend=False).grid(axis='x')
plt.show()
else:
print("--- No word recognized ---")**### 5\. produce html visualization**
text = []
for word in lst_corpus[0]:
weight = dic_word_weigth.get(word)
if weight is not None:
text.append('<b><span style="background-color:rgba(100,149,237,' + str(weight) + ');">' + word + '</span></b>')
else:
text.append(word)
text = ' '.join(text)**### 6\. visualize on notebook** print("**\033**[1m"+"Text with highlighted words")
from IPython.core.display import display, HTML
display(HTML(text))***
就像以前一样,单词“克林顿”和“共和党”激活了模型的神经元,但这次“高”和“班加西”也被认为与预测略有关联。**
语言模型
语言模型,或语境化/动态单词嵌入,克服了经典单词嵌入方法的最大限制:多义词歧义消除,一个具有不同含义的单词(例如“ bank 或“ stick ”)仅通过一个向量来识别。最受欢迎的方法之一是 ELMO (2018),它没有应用固定的嵌入,而是使用双向 LSTM,查看整个句子,然后为每个单词分配一个嵌入。**
进入变形金刚:谷歌论文 提出的一种新的建模技术(2017)中展示了顺序模型(如 LSTM)可以完全被注意力机制取代,甚至获得更好的性能。****
谷歌的 伯特 (来自变形金刚的双向编码器表示,2018)结合了 ELMO 上下文嵌入和几个变形金刚,加上它的双向(这对变形金刚来说是一个很大的新奇)。BERT 分配给单词的向量是整个句子的函数,因此,基于上下文,一个单词可以有不同的向量。让我们用变压器试试吧:**
***txt = **"bank river"****## bert tokenizer**
tokenizer = transformers.**BertTokenizer**.**from_pretrained**('bert-base-uncased', do_lower_case=True)**## bert model**
nlp = transformers.**TFBertModel**.**from_pretrained**('bert-base-uncased')**## return hidden layer with embeddings**
input_ids = np.array(tokenizer.encode(txt))[None,:]
embedding = nlp(input_ids)embedding[0][0]***
如果我们将输入文本更改为“银行资金”,我们得到的结果是:**
为了完成文本分类任务,您可以以 3 种不同的方式使用 BERT:
- 训练它从划痕,并使用它作为分类器。
- 提取单词 embedding,并在嵌入层中使用它们(就像我对 Word2Vec 所做的那样)。
- 微调预训练模型(迁移学习)。
我将采用后者,并从一个预训练的较轻版本的 BERT 进行迁移学习,称为distilt-BERT(6600 万个参数,而不是 1.1 亿个!).
*****## distil-bert tokenizer**
tokenizer = transformers.**AutoTokenizer**.**from_pretrained**('distilbert-base-uncased', do_lower_case=True)***
像往常一样,在装配模型之前,有一些特征工程工作要做,但是这次会有点棘手。为了说明我要做的事情,让我们以我们最喜欢的句子“我喜欢这篇文章”为例,它必须转换为 3 个向量(id、掩码、段):**
形状:3 x 序列长度
首先,我们需要选择序列的最大长度。这次我将选择一个更大的数字(即 50 ),因为 BERT 会将未知单词拆分成子标记,直到找到一个已知的单词。例如,如果给定一个像" zzdata "这样的造词,伯特会把它拆分成[" z "," ##z "," ##data "]。此外,我们必须在输入文本中插入特殊标记,然后生成掩码和分段。最后,将所有这些放在一个张量中以获得特征矩阵,该矩阵将具有 3(id、掩码、分段)x 语料库中的文档数量 x 序列长度的形状。
请注意,我使用原始文本作为语料库(到目前为止,我一直使用 clean_text 列)。
***corpus = dtf_train["**text**"]
maxlen = 50 **## add special tokens**
maxqnans = np.int((maxlen-20)/2)
corpus_tokenized = ["[CLS] "+
" ".join(tokenizer.tokenize(re.sub(r'[^\w\s]+|\n', '',
str(txt).lower().strip()))[:maxqnans])+
" [SEP] " for txt in corpus]
**## generate masks**
masks = [[1]*len(txt.split(" ")) + [0]*(maxlen - len(
txt.split(" "))) for txt in corpus_tokenized]
**## padding**
txt2seq = [txt + " [PAD]"*(maxlen-len(txt.split(" "))) if len(txt.split(" ")) != maxlen else txt for txt in corpus_tokenized]
**## generate idx**
idx = [tokenizer.encode(seq.split(" ")) for seq in txt2seq]
**## generate segments**
segments = []
for seq in txt2seq:
temp, i = [], 0
for token in seq.split(" "):
temp.append(i)
if token == "[SEP]":
i += 1
segments.append(temp)**## feature matrix**
X_train = [np.asarray(idx, dtype='int32'),
np.asarray(masks, dtype='int32'),
np.asarray(segments, dtype='int32')]***
特征矩阵 X_train 的形状为 3×34,265×50。我们可以从特征矩阵中检查随机观察值:
***i = 0print("txt: ", dtf_train["text"].iloc[0])
print("tokenized:", [tokenizer.convert_ids_to_tokens(idx) for idx in X_train[0][i].tolist()])
print("idx: ", X_train[0][i])
print("mask: ", X_train[1][i])
print("segment: ", X_train[2][i])***
您可以将相同的代码应用于 dtf_test["text"]以获得 X_test 。
现在,我要从预先训练好的 BERT 开始,用迁移学习构建深度学习模型。基本上,我将使用平均池将 BERT 的输出总结为一个向量,然后添加两个最终的密集层来预测每个新闻类别的概率。****
如果您想使用 BERT 的原始版本,下面是代码(记得用正确的标记器重做特性工程):
*****## inputs**
idx = layers.**Input**((50), dtype="int32", name="input_idx")
masks = layers.**Input**((50), dtype="int32", name="input_masks")
segments = layers.Input((50), dtype="int32", name="input_segments")**## pre-trained bert**
nlp = transformers.**TFBertModel.from_pretrained**("bert-base-uncased")
bert_out, _ = nlp([idx, masks, segments])**## fine-tuning**
x = layers.**GlobalAveragePooling1D**()(bert_out)
x = layers.**Dense**(64, activation="relu")(x)
y_out = layers.**Dense**(len(np.unique(y_train)),
activation='softmax')(x)**## compile**
model = models.**Model**([idx, masks, segments], y_out)for layer in model.layers[:4]:
layer.trainable = Falsemodel.compile(loss='sparse_categorical_crossentropy',
optimizer='adam', metrics=['accuracy'])model.summary()***
正如我所说,我将使用更简单的版本,distilt-BERT:
*****## inputs**
idx = layers.**Input**((50), dtype="int32", name="input_idx")
masks = layers.**Input**((50), dtype="int32", name="input_masks")**## pre-trained bert with config**
config = transformers.DistilBertConfig(dropout=0.2,
attention_dropout=0.2)
config.output_hidden_states = Falsenlp = transformers.**TFDistilBertModel.from_pretrained**('distilbert-
base-uncased', config=config)
bert_out = nlp(idx, attention_mask=masks)[0]**## fine-tuning**
x = layers.**GlobalAveragePooling1D**()(bert_out)
x = layers.**Dense**(64, activation="relu")(x)
y_out = layers.**Dense**(len(np.unique(y_train)),
activation='softmax')(x)**## compile**
model = models.**Model**([idx, masks], y_out)for layer in model.layers[:3]:
layer.trainable = Falsemodel.compile(loss='sparse_categorical_crossentropy',
optimizer='adam', metrics=['accuracy'])model.summary()***
让我们训练、测试、评估这个坏小子(评估的代码相同):**
*****## encode y**
dic_y_mapping = {n:label for n,label in
enumerate(np.unique(y_train))}
inverse_dic = {v:k for k,v in dic_y_mapping.items()}
y_train = np.array([inverse_dic[y] for y in y_train])**## train**
training = model.fit(x=X_train, y=y_train, batch_size=64,
epochs=1, shuffle=True, verbose=1,
validation_split=0.3)**## test**
predicted_prob = model.predict(X_test)
predicted = [dic_y_mapping[np.argmax(pred)] for pred in
predicted_prob]***
BERT 的性能比以前的型号略好,事实上,它比其他型号能识别更多的科技新闻。
结论
这篇文章是演示如何将不同的 NLP 模型应用到多类分类用例的教程。我比较了 3 种流行的方法:使用 Tf-Idf 的单词包、使用 Word2Vec 的单词嵌入和使用 BERT 的语言模型。我经历了特征工程&选择,模型设计&测试,评估&可解释性,比较每一步中的 3 个模型(如果可能的话)。**
我希望你喜欢它!如有问题和反馈,或者只是分享您感兴趣的项目,请随时联系我。
👉我们来连线👈
本文是 Python 的系列文章 NLP 的一部分,参见:
*** [## 带 NLP 的 AI 聊天机器人:语音识别+变形金刚
用 Python 构建一个会说话的聊天机器人,与你的人工智能进行对话
towardsdatascience.com](/ai-chatbot-with-nlp-speech-recognition-transformers-583716a299e9) [## 使用 NLP 的文本摘要:TextRank vs Seq2Seq vs BART
使用 Python、Gensim、Tensorflow、Transformers 进行自然语言处理
towardsdatascience.com](/text-summarization-with-nlp-textrank-vs-seq2seq-vs-bart-474943efeb09) [## 使用自然语言处理的文本分析和特征工程
语言检测,文本清理,长度,情感,命名实体识别,N-grams 频率,词向量,主题…
towardsdatascience.com](/text-analysis-feature-engineering-with-nlp-502d6ea9225d) [## 用于无模型训练的文本分类的 BERT
如果没有带标签的训练集,请使用 BERT、单词嵌入和向量相似度
towardsdatascience.com](/text-classification-with-no-model-training-935fe0e42180)***
用于无模型训练的文本分类的 BERT
如果没有带标签的训练集,请使用 BERT、单词嵌入和向量相似度
摘要
您是否因为没有带标签的数据集而难以对文本数据进行分类?在本文中,我将使用 BERT 和 Python 解释如何执行一种基于相似性的“无监督”文本分类。
作者图片
【NLP(自然语言处理) 是人工智能领域,研究计算机与人类语言之间的交互,特别是如何给计算机编程以处理和分析大量自然语言数据。NLP 通常用于文本数据的分类。文本分类就是根据文本数据的内容给文本数据分配类别的问题。为了执行分类用例,您需要一个用于机器学习模型训练的标记数据集。如果你没有,会发生什么?
这种情况在现实世界中发生的次数比你想象的要多。如今,人工智能被大肆宣传,以至于企业甚至在没有数据的情况下也想使用它。特别是,大多数非技术人员并没有完全理解“目标变量”的概念,以及它在监督机器学习中是如何使用的。那么,当你有文本数据但没有标签时,如何构建一个分类器呢?在本教程中,我将解释一种应用 W2V 和 BERT 通过词向量相似度对文本进行分类的策略。
我将展示一些有用的 Python 代码,这些代码可以很容易地应用于其他类似的情况(只需复制、粘贴、运行),并通过注释遍历每一行代码,以便您可以复制这个示例(下面是完整代码的链接)。
[## mdipietro 09/data science _ 人工智能 _ 实用工具
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
我将使用“新闻类别数据集,其中为您提供了从赫芬顿邮报获得的 2012 年至 2018 年的新闻标题,并要求您将它们分类到正确的类别,因此这是一个多类别分类问题(下面的链接)。
根据标题和简短描述识别新闻的类型
www.kaggle.com](https://www.kaggle.com/rmisra/news-category-dataset)
特别是,我将经历:
- 设置:导入包,读取数据。
- 预处理:清理文本数据。
- 创建目标集群:使用 Word2Vec 和 gensim 构建目标变量。
- 特征工程:用变形金刚和 BERT 嵌入单词。
- 模型设计和测试:通过余弦相似性将观察值分配给集群,并评估性能。
- 可解释性:理解模型如何产生结果。
设置
首先,我需要导入以下包:
**## for data** import **json** import **pandas** as pd
import **numpy** as np
from **sklearn** import metrics, manifold**## for processing** import **re**
import **nltk****## for plotting**
import **matplotlib**.pyplot as plt
import **seaborn** as sns**## for w2v**
import **gensim** import gensim.downloader as gensim_api**## for bert**
import **transformers**
数据集包含在一个 json 文件中,所以我将首先用 json 把它读入一个字典列表,然后把它转换成一个 pandas Dataframe。
lst_dics = []
with **open**('data.json', mode='r', errors='ignore') as json_file:
for dic in json_file:
lst_dics.append( json**.loads**(dic) )**## print the first one**
lst_dics[0]
作者图片
原始数据集包含超过 30 个类别,但是出于本教程的目的,我将使用 3 个类别的子集:娱乐、政治和技术。
**## create dtf**
dtf = pd.DataFrame(lst_dics)**## filter categories**
dtf = dtf[ dtf["category"].isin(['**ENTERTAINMENT**','**POLITICS**','**TECH**']) ][["category","headline"]]**## rename columns**
dtf = dtf.rename(columns={"category":"**y**", "headline":"**text**"})**## print 5 random rows**
dtf.sample(5)
作者图片
如您所见,数据集还包括一个目标变量。我不会将它用于建模,只是用于性能评估。
所以我们有一些原始的文本数据,我们的任务是把它分成我们一无所知的 3 类(娱乐、政治、科技)。这是我计划要做的:
- 清理数据并将其嵌入向量空间,
- 为每个类别创建一个主题聚类并将其嵌入向量空间,
- 计算每个文本向量和主题聚类之间的相似度,然后将其分配给最接近的聚类。
作者图片
这就是为什么我称之为“一种无监督的文本分类”。这是一个非常基本的想法,但是执行起来会很棘手。
现在都准备好了,让我们开始吧。
预处理
绝对的第一步是对数据进行预处理:清理文本、删除停用词和应用词汇化。我将编写一个函数,并将其应用于整个数据集。
**'''
Preprocess a string.
:parameter
:param text: string - name of column containing text
:param lst_stopwords: list - list of stopwords to remove
:param flg_stemm: bool - whether stemming is to be applied
:param flg_lemm: bool - whether lemmitisation is to be applied
:return
cleaned text
'''**
def **utils_preprocess_text**(text, flg_stemm=False, flg_lemm=True, lst_stopwords=None):
**## clean (convert to lowercase and remove punctuations and
characters and then strip)**
text = re.sub(r'[^\w\s]', '', str(text).lower().strip())
**## Tokenize (convert from string to list)**
lst_text = text.split() **## remove Stopwords**
if lst_stopwords is not None:
lst_text = [word for word in lst_text if word not in
lst_stopwords]
**## Stemming (remove -ing, -ly, ...)**
if flg_stemm == True:
ps = nltk.stem.porter.PorterStemmer()
lst_text = [ps.stem(word) for word in lst_text]
**## Lemmatisation (convert the word into root word)**
if flg_lemm == True:
lem = nltk.stem.wordnet.WordNetLemmatizer()
lst_text = [lem.lemmatize(word) for word in lst_text]
**## back to string from list**
text = " ".join(lst_text)
return text
该函数从语料库中删除一组给定的单词。我可以用 nltk 为英语词汇创建一个通用停用词列表(我们可以通过添加或删除单词来编辑这个列表)。
lst_stopwords = **nltk**.corpus.stopwords.words("**english**")
lst_stopwords
作者图片
现在,我将对整个数据集应用该函数,并将结果存储在一个名为“ text_clean 的新列中,我将把它用作语料库。
dtf["**text_clean**"] = dtf["text"].apply(lambda x:
**utils_preprocess_text**(x, flg_stemm=False, **flg_lemm=True**,
**lst_stopwords=lst_stopwords**))dtf.head()
作者图片
我们有了预处理的语料库,因此下一步是构建目标变量。基本上,我们在这里:
作者图片
创建目标集群
本节的目标是创建一些可以代表每个类别的上下文的关键字。通过进行一些文本分析,你可以很容易地发现,出现频率最高的 3 个词是“电影”、“王牌”和“苹果”(关于详细的文本分析教程,你可以查看这篇文章)。我建议从这些关键词开始。
让我们以政治类别为例:单词“ trump ”可以有不同的含义,因此我们需要添加关键字来避免多义性问题(例如,“ donald ”、“ republican ”、“ white house ”、“ obama ”)。这项任务可以手动执行,或者您可以使用预先训练的 NLP 模型的帮助。您可以从genism-data中加载一个预训练的单词嵌入模型,如下所示:
*nlp = gensim_api.load("**glove-wiki-gigaword-300**")*
gensim 包有一个非常方便的功能,可以将任何给定单词的最相似的单词返回到词汇表中。
*nlp.**most_similar**(["**obama**"], topn=3)*
作者图片
我将使用它为每个类别创建一个关键字字典:
***## Function to apply**
def **get_similar_words**(lst_words, top, nlp):
lst_out = lst_words
for tupla in nlp.most_similar(lst_words, topn=top):
lst_out.append(tupla[0])
return list(set(lst_out)) **## Create Dictionary {category:[keywords]}** dic_clusters = {}dic_clusters["**ENTERTAINMENT**"] = get_similar_words([**'celebrity','cinema','movie','music'**],
top=30, nlp=nlp)dic_clusters[**"POLITICS"**] = get_similar_words([**'gop','clinton','president','obama','republican'**]
, top=30, nlp=nlp)dic_clusters["**TECH**"] = get_similar_words([**'amazon','android','app','apple','facebook',
'google','tech'**],
top=30, nlp=nlp) **## print some**
for k,v in dic_clusters.items():
print(k, ": ", v[0:5], "...", len(v))*
作者图片
让我们通过应用降维算法(即 TSNE )来尝试在 2D 空间中可视化那些关键词。我们希望确保集群之间能够很好地分离。
***## word embedding** tot_words = [word for v in **dic_clusters**.values() for word in v]
X = nlp[tot_words] **## pca**
pca = manifold.**TSNE**(perplexity=40, n_components=2, init='pca')
X = pca.fit_transform(X) **## create dtf**
dtf = pd.DataFrame()
for k,v in **dic_clusters**.items():
size = len(dtf) + len(v)
dtf_group = pd.DataFrame(X[len(dtf):size], columns=["x","y"],
index=v)
dtf_group["cluster"] = k
dtf = dtf.append(dtf_group) **## plot**
fig, ax = plt.subplots()
sns.**scatterplot**(data=dtf, x="x", y="y", hue="cluster", ax=ax)ax.legend().texts[0].set_text(None)
ax.set(xlabel=None, ylabel=None, xticks=[], xticklabels=[],
yticks=[], yticklabels=[])for i in range(len(dtf)):
ax.annotate(dtf.index[i],
xy=(dtf["x"].iloc[i],dtf["y"].iloc[i]),
xytext=(5,2), textcoords='offset points',
ha='right', va='bottom')*
作者图片
酷,他们看起来已经足够孤立了。娱乐集群比政治集群更接近科技集群,这是有道理的,因为像“苹果”和“ youtube ”这样的词可以同时出现在科技和娱乐新闻中。
特征工程
是时候将我们预处理的语料库和我们创建的目标聚类嵌入到同一个向量空间中了。基本上,我们是这样做的:
作者图片
是的,我在用 BERT 做这个。的确,您可以利用任何单词嵌入模型(即 Word2Vec、Glove 等),甚至是我们已经加载的定义关键字的模型,所以为什么要费心使用如此沉重和复杂的语言模型呢?这是因为 BERT 没有应用固定的嵌入,而是查看整个句子,然后给每个单词分配一个嵌入。因此,BERT 分配给一个单词的向量是整个句子的函数,因此一个单词可以基于上下文具有不同的向量。
我将使用包 transformers 加载原始预训练版本的 BERT,并给出一个动态嵌入的示例:
*tokenizer = transformers.**BertTokenizer**.from_pretrained('**bert-base-
uncased'**, do_lower_case=True)nlp = transformers.**TFBertModel**.from_pretrained(**'bert-base-uncased'**)*
让我们使用该模型将字符串" river bank "转换成向量,并打印分配给单词" bank "的向量:
*txt = **"river bank"****## tokenize**
idx = tokenizer.encode(txt)
print("tokens:", tokenizer.convert_ids_to_tokens(idx))
print("ids :", tokenizer.encode(txt))**## word embedding**
idx = np.array(idx)[None,:]
embedding = nlp(idx)
print("shape:", embedding[0][0].shape)**## vector of the second input word**
embedding[0][0][2]*
作者图片
如果您对字符串“金融银行”做同样的处理,您会发现分配给单词“银行的向量因上下文而异。请注意,BERT 记号赋予器在句子的开头和结尾插入特殊记号,其向量空间的维数为 768(为了更好地理解 BERT 如何处理文本,您可以查看这篇文章)。
作者图片
说了这么多,计划是用 BERT Word Embedding 用一个数组(shape: number of tokens x 768)表示每篇文本,然后把每篇文章汇总成一个均值向量。
作者图片
因此,最终的特征矩阵将是一个形状为:文档数(或均值向量)x 768 的数组。
***## function to apply** def **utils_bert_embedding**(txt, tokenizer, nlp):
idx = tokenizer.encode(txt)
idx = np.array(idx)[None,:]
embedding = nlp(idx)
X = np.array(embedding[0][0][1:-1])
return X**## create list of news vector**
lst_mean_vecs = [**utils_bert_embedding**(txt, tokenizer, nlp)**.mean(0)**
for txt in dtf["**text_clean**"]]**## create the feature matrix (n news x 768)**
X = np.array(lst_mean_vecs)*
我们可以对目标集群中的关键字做同样的事情。事实上,每个标签都由一个单词列表来标识,帮助 BERT 理解集群中的上下文。因此,我将创建一个字典标签:聚类均值向量。
*dic_y = {k:**utils_bert_embedding**(v, tokenizer, nlp)**.mean(0)** for k,v
in dic_clusters.items()}*
我们开始时只有一些文本数据和 3 个字符串(“娱乐”、“政治”、“技术”),现在我们有了一个特征矩阵和一个目标变量… ish。
模型设计和测试
最后,是时候建立一个模型,根据与每个目标聚类的相似性对新闻进行分类了。
作者图片
我将使用 余弦相似度 ,这是一种基于两个非零向量之间的角度余弦的相似性度量,它等于归一化为长度都为 1 的相同向量的内积。您可以轻松地使用 scikit 的余弦相似性实现-learn ,它采用 2 个数组(或向量)并返回一个分数数组(或单个分数)。在这种情况下,输出将是一个具有形状的矩阵:新闻数量 x 标签数量(3,娱乐/政治/技术)。换句话说,每行将代表一篇文章,并包含每个目标聚类的一个相似性得分。
为了运行通常的评估指标(准确性、AUC、精确度、召回率等),我们必须重新调整每一行的分数,使它们的总和为 1,并决定文章的类别。我将选择得分最高的一个,但设置一些最低阈值并忽略得分非常低的预测可能是明智的。
作者图片
***#--- Model Algorithm ---#****## compute cosine similarities**
similarities = np.array(
[metrics.pairwise.**cosine_similarity**(X, y).T.tolist()[0]
for y in dic_y.values()]
).T**## adjust and rescale**
labels = list(dic_y.keys())
for i in range(len(similarities)): **### assign randomly if there is no similarity**
if sum(similarities[i]) == 0:
similarities[i] = [0]*len(labels)
similarities[i][np.random.choice(range(len(labels)))] = 1 **### rescale so they sum = 1**
similarities[i] = similarities[i] / sum(similarities[i]) **## classify the label with highest similarity score** predicted_prob = similarities
predicted = [labels[np.argmax(pred)] for pred in predicted_prob]*
就像在经典的监督用例中一样,我们有一个具有预测概率的对象(这里它们是调整后的相似性分数),另一个具有预测标签。让我们检查一下我们做得如何:
*y_test = dtf[**"y"**].values
classes = np.unique(y_test)
y_test_array = pd.get_dummies(y_test, drop_first=False).values **## Accuracy, Precision, Recall**
accuracy = metrics.accuracy_score(y_test, predicted)
auc = metrics.roc_auc_score(y_test, predicted_prob,
multi_class="ovr")
print("Accuracy:", round(accuracy,2))
print("Auc:", round(auc,2))
print("Detail:")
print(metrics.classification_report(y_test, predicted)) **## Plot confusion matrix**
cm = metrics.confusion_matrix(y_test, predicted)
fig, ax = plt.subplots()
sns.heatmap(cm, annot=True, fmt='d', ax=ax, cmap=plt.cm.Blues,
cbar=False)
ax.set(xlabel="Pred", ylabel="True", xticklabels=classes,
yticklabels=classes, title="Confusion matrix")
plt.yticks(rotation=0)
fig, ax = plt.subplots(nrows=1, ncols=2) **## Plot roc**
for i in range(len(classes)):
fpr, tpr, thresholds = metrics.roc_curve(y_test_array[:,i],
predicted_prob[:,i])
ax[0].plot(fpr, tpr, lw=3,
label='{0} (area={1:0.2f})'.format(classes[i],
metrics.auc(fpr, tpr))
)
ax[0].plot([0,1], [0,1], color='navy', lw=3, linestyle='--')
ax[0].set(xlim=[-0.05,1.0], ylim=[0.0,1.05],
xlabel='False Positive Rate',
ylabel="True Positive Rate (Recall)",
title="Receiver operating characteristic")
ax[0].legend(loc="lower right")
ax[0].grid(True) **## Plot precision-recall curve** for i in range(len(classes)):
precision, recall, thresholds = metrics.precision_recall_curve(
y_test_array[:,i], predicted_prob[:,i])
ax[1].plot(recall, precision, lw=3,
label='{0} (area={1:0.2f})'.format(classes[i],
metrics.auc(recall, precision))
)
ax[1].set(xlim=[0.0,1.05], ylim=[0.0,1.05], xlabel='Recall',
ylabel="Precision", title="Precision-Recall curve")
ax[1].legend(loc="best")
ax[1].grid(True)
plt.show()*
**
作者图片
好吧,我第一个说这不是我见过的最好的准确度。另一方面,考虑到我们没有训练任何模型,我们甚至虚构了目标变量,这一点也不差。主要问题是分类为娱乐的 4k 以上的政治观察,但这些性能可以通过微调这两个类别的关键字来轻松改善。
可解释性
让我们试着理解是什么让我们的算法用一个类别而不是其他类别对新闻进行分类。让我们从语料库中随机观察一下:
*i = 7**txt_instance** = dtf[**"text_clean"**].iloc[i]print("True:", y_test[i], "--> Pred:", predicted[i], "|
Similarity:", round(np.max(predicted_prob[i]),2))
print(txt_instance)*
作者图片
这是一个正确分类的政治观察。大概,“共和党”和“克林顿”这两个词给了伯特正确的暗示。我将在 2D 空间中可视化文章的平均向量,并绘制与目标聚类的最高相似度。
***## create embedding Matrix** y = np.concatenate([embedding_bert(v, tokenizer, nlp) for v in
dic_clusters.values()])
X = embedding_bert(txt_instance, tokenizer,
nlp).mean(0).reshape(1,-1)
M = np.concatenate([y,X]) **## pca**
pca = manifold.**TSNE**(perplexity=40, n_components=2, init='pca')
M = pca.fit_transform(M)
y, X = M[:len(y)], M[len(y):] **## create dtf clusters**
dtf = pd.DataFrame()
for k,v in dic_clusters.items():
size = len(dtf) + len(v)
dtf_group = pd.DataFrame(y[len(dtf):size], columns=["x","y"],
index=v)
dtf_group["cluster"] = k
dtf = dtf.append(dtf_group) **## plot clusters**
fig, ax = plt.subplots()
sns.**scatterplot**(data=dtf, x="x", y="y", hue="cluster", ax=ax)
ax.legend().texts[0].set_text(None)
ax.set(xlabel=None, ylabel=None, xticks=[], xticklabels=[],
yticks=[], yticklabels=[])
for i in range(len(dtf)):
ax.annotate(dtf.index[i],
xy=(dtf["x"].iloc[i],dtf["y"].iloc[i]),
xytext=(5,2), textcoords='offset points',
ha='right', va='bottom') **## add txt_instance** ax.scatter(x=X[0][0], y=X[0][1], c="red", linewidth=10)
ax.annotate("x", xy=(X[0][0],X[0][1]),
ha='center', va='center', fontsize=25) **## calculate similarity** sim_matrix = metrics.pairwise.**cosine_similarity**(X, y) **## add top similarity**
for row in range(sim_matrix.shape[0]): **### sorted {keyword:score}**
dic_sim = {n:sim_matrix[row][n] for n in
range(sim_matrix.shape[1])}
dic_sim = {k:v for k,v in sorted(dic_sim.items(),
key=lambda item:item[1], reverse=True)} **### plot lines**
for k in dict(list(dic_sim.items())[0:5]).keys():
p1 = [X[row][0], X[row][1]]
p2 = [y[k][0], y[k][1]]
ax.plot([p1[0],p2[0]], [p1[1],p2[1]], c="red", alpha=0.5)plt.show()*
作者图片
让我们放大一下感兴趣的集群:
作者图片
总的来说,我们可以说均值向量非常类似于政治聚类。让我们将文章分解成令牌,看看哪些令牌“激活”了正确的集群。
***## create embedding Matrix** y = np.concatenate([embedding_bert(v, tokenizer, nlp) for v in
dic_clusters.values()])
X = embedding_bert(txt_instance, tokenizer,
nlp).mean(0).reshape(1,-1)
M = np.concatenate([y,X]) **## pca**
pca = manifold.**TSNE**(perplexity=40, n_components=2, init='pca')
M = pca.fit_transform(M)
y, X = M[:len(y)], M[len(y):] **## create dtf clusters**
dtf = pd.DataFrame()
for k,v in dic_clusters.items():
size = len(dtf) + len(v)
dtf_group = pd.DataFrame(y[len(dtf):size], columns=["x","y"],
index=v)
dtf_group["cluster"] = k
dtf = dtf.append(dtf_group) **## add txt_instance** tokens = tokenizer.convert_ids_to_tokens(
tokenizer.encode(txt_instance))[1:-1]
dtf = pd.DataFrame(X, columns=["x","y"], index=tokens)
dtf = dtf[~dtf.index.str.contains("#")]
dtf = dtf[dtf.index.str.len() > 1]
X = dtf.values
ax.scatter(x=dtf["x"], y=dtf["y"], c="red")
for i in range(len(dtf)):
ax.annotate(dtf.index[i],
xy=(dtf["x"].iloc[i],dtf["y"].iloc[i]),
xytext=(5,2), textcoords='offset points',
ha='right', va='bottom') **## calculate similarity** sim_matrix = metrics.pairwise.**cosine_similarity**(X, y) **## add top similarity**
for row in range(sim_matrix.shape[0]): **### sorted {keyword:score}**
dic_sim = {n:sim_matrix[row][n] for n in
range(sim_matrix.shape[1])}
dic_sim = {k:v for k,v in sorted(dic_sim.items(),
key=lambda item:item[1], reverse=True)} **### plot lines**
for k in dict(list(dic_sim.items())[0:5]).keys():
p1 = [X[row][0], X[row][1]]
p2 = [y[k][0], y[k][1]]
ax.plot([p1[0],p2[0]], [p1[1],p2[1]], c="red", alpha=0.5)plt.show()*
作者图片
正如我们所想,文本中有一些词明显与政治相关,但其他一些词更类似于娱乐的一般上下文。
**
作者图片
结论
这篇文章是一个教程,演示了当一个带标签的训练集不可用时如何执行文本分类。
我使用预先训练的单词嵌入模型来构建一组关键字,以将目标变量置于上下文中。然后我用预先训练好的 BERT 语言模型把那些词和语料库转换到同一个向量空间。最后,我计算文本和关键词之间的余弦相似度,以确定每篇文章的上下文,并使用该信息来标记新闻。
这种策略不是最有效的,但它肯定是有效的,因为它能让你迅速获得好的结果。此外,一旦获得标记数据集,该算法可以用作监督模型的基线。
我希望你喜欢它!如有问题和反馈,或者只是分享您感兴趣的项目,请随时联系我。
👉我们来连线👈
本文是使用 Python 的NLP系列的一部分,参见:
* [## 使用 NLP 的文本摘要:TextRank vs Seq2Seq vs BART
使用 Python、Gensim、Tensorflow、Transformers 进行自然语言处理
towardsdatascience.com](/text-summarization-with-nlp-textrank-vs-seq2seq-vs-bart-474943efeb09) [## 使用自然语言处理的文本分析和特征工程
语言检测,文本清理,长度,情感,命名实体识别,N-grams 频率,词向量,主题…
towardsdatascience.com](/text-analysis-feature-engineering-with-nlp-502d6ea9225d) [## 基于自然语言处理的文本分类:Tf-Idf vs Word2Vec vs BERT
预处理、模型设计、评估、词袋的可解释性、词嵌入、语言模型
towardsdatascience.com](/text-classification-with-nlp-tf-idf-vs-word2vec-vs-bert-41ff868d1794) [## 带 NLP 的 AI 聊天机器人:语音识别+变形金刚
用 Python 构建一个会说话的聊天机器人,与你的人工智能进行对话
towardsdatascience.com](/ai-chatbot-with-nlp-speech-recognition-transformers-583716a299e9)*
PyTorch 中基于 LSTMs 的文本分类
在 PyTorch 中使用 LSTMs 实现的文本分类基线模型
问题仍然悬而未决:如何学习语义学?什么是语义学?基于 DL 的模型能够学习语义吗?
介绍
这篇博客的目的是解释如何基于 LSTMs 构建一个文本分类器,以及如何使用 PyTorch 框架来构建它。
我想从下面这个问题开始:如何对文本进行分类?在不同的前提下,从不同的角度提出了几种方法,但是哪一种是最合适的呢?。停下来问自己一个有趣的问题:作为人类,我们如何对文本进行分类?,我们的大脑在对文本进行分类时考虑了什么?。这类问题很难回答。
目前,我们可以访问一系列不同的文本类型,如电子邮件、电影评论、社交媒体、书籍等。在这个意义上,文本分类问题将由想要分类的内容来确定(例如是否想要对给定文本的极性进行分类?是不是打算把一套影评按类别分类?是否打算按主题对一组文本进行分类?)。在这方面,文本分类的问题大部分时间被归类在以下任务下:
- 情感分析
- 新闻分类
- 话题分析
- 问题解答
- 自然语言推理
为了深入这个热门话题,我真的推荐看一看这篇论文: 基于深度学习的文本分类:综合复习 。
方法学
该模型的两个关键是:标记化和递归神经网络。标记化是指将文本拆分成一组句子或单词(即标记)的过程。在这方面,标记化技术可以在序列级或单词级应用。为了理解标记化的基础,可以看一下: 信息检索简介 。
另一方面,RNNs(递归神经网络)是一种神经网络,众所周知,它能很好地处理序列数据,例如文本数据。在这种情况下,实现了一种特殊的 RNN,即 LSTMs(长短期记忆)。lstm 是 RNNs 的改进版本之一,本质上 lstm 在处理长句时表现更好。为了更深入的了解什么是 RNNs 和 LSTMs,可以看一下: 了解 LSTMs 网络。
因为这个博客的想法是提出一个文本分类的基线模型,所以文本预处理阶段是基于标记化技术的,这意味着每个文本句子将被标记化,然后每个标记将被转换成其基于索引的表示。然后,每个基于索引的记号语句将顺序地通过嵌入层,该嵌入层将输出每个记号的嵌入表示,这些记号通过两层 LSTM 神经网络,然后最后的 LSTM 隐藏状态将通过双线层神经网络,该网络输出由 sigmoid 激活函数过滤的单个值。下图描述了模型架构:
图一。模型架构
预处理
这个项目中使用的数据集取自一场 k aggle 竞赛 ,该竞赛旨在预测哪些推文是关于真实灾难的,哪些不是。本质上,数据集是关于一组用 1 和 0 标记的原始格式的推文(1 表示真正的灾难,0 表示不是真正的灾难)。看一下数据集的头部,它看起来像:
图二。推文数据集的头
正如我们所看到的,有些列必须删除,因为它们毫无意义,因此删除不必要的列后,结果数据集将如下所示:
图 3。格式化数据集
此时,我们已经可以应用标记化技术,并将每个标记转换成基于索引的表示;下面的代码片段解释了这个过程:
代码片段 1。预处理
有一些固定的超参数值得一提。例如, max_len = 10 指的是每个序列的最大长度,而 max_words = 100 指的是给定整个语料库要考虑的前 100 个频繁出现的单词。函数 prepare_tokens() 将整个语料库转换成一组记号序列。函数 sequence_to_token() 将每个标记转换成其索引表示。
模型
作为输入层,它被实现为嵌入层。这个嵌入层获取每个令牌,并将其转换为嵌入的表示。这种嵌入的表示然后通过两个堆叠的 LSTM 层。最后,LSTM 的最后一个隐藏状态通过一个二线性层神经网络。下面的代码片段显示了用 PyTorch 编写的上述模型架构。
代码片段 2。模型架构
因此,让我们分析一下展示的模型架构的一些重要部分。在第 16 行中,嵌入层被初始化,它接收参数: input_size ,它指的是词汇表的大小, hidden_dim ,它指的是输出向量的维数,以及 padding_idx ,它用零来完成不满足所需序列长度的序列。
在第 17 行中,LSTM 层被初始化,它接收参数: input_size ,它指的是嵌入令牌的维度, hidden_size ,它指的是隐藏和单元状态的维度, num_layers ,它指的是堆叠的 LSTM 层的数量,以及 batch_first ,它指的是输入向量的第一维度,在这种情况下,它指的是批量大小。
在第 18 行和第 19 行,线性层被初始化,每个层接收参数: in_features 和 out_features ,分别表示输入和输出尺寸。
培训阶段
为了准备好训练阶段,首先,我们需要准备如何将序列输入到模型中。为此,PyTorch 提供了两个非常有用的类: Dataset 和 DataLoader。Datasetclassclass的目的是提供一种简单的批量迭代数据集的方法。数据加载器的目的是创建数据集类的可迭代对象。下面的代码片段展示了这两个类的最小化实现。
代码片段 3。数据集和数据集加载器
现在,是时候迭代训练集了。首先,让我们看看培训阶段是什么样子的:
代码片段 4。培训阶段
第 2 行定义了优化器。从第 4 行开始,实现了历元上的循环。值得一提的是,在 PyTorch 中,我们需要"打开训练模式",正如您在第 9 行中看到的,尤其是当我们必须从"训练模式切换到"评估模式(我们将在后面看到)时,这样做是很有必要的。本质上,训练模式允许对梯度的更新,而评估模式取消对梯度的更新。
需要强调的是,在第 11 行,我们使用由 DatasetLoader 创建的对象进行迭代。在 PyTorch 中,计算损失函数、计算梯度、通过实现一些优化器方法来更新参数以及使梯度为零是相对容易的。如我们所见,在第 20 行中,通过将 binary_cross_entropy 实现为损失函数来计算损失,在第 24 行中,误差被向后传播(即梯度被计算),在第 30 行中,通过将 RMSprop 实现为优化器来更新每个参数,然后梯度被释放以开始新的时期。
估价
评估部分与我们在培训阶段所做的非常相似,主要区别是从培训模式变为评估模式。**
代码片段 5。评估阶段
正如我们所见,在第 6 行中,模型被更改为评估模式,同时跳过第 9 行中的渐变更新。然后,通过 DatasetLoader 对象迭代测试集(第 12 行),同样,预测值保存在第 21 行的预测列表中。
最后,我们只需要计算精度。为了记住精度是如何计算的,让我们看一下公式:
*Accuracy = (True Positives + True Negatives) / Number of samples*
在这方面,精度计算如下:
代码片段 6。精确度计算
结论
在这篇博客中,我们已经解释了文本分类的重要性,以及在不同观点下解决文本分类问题的不同方法。
以 LSTMs 神经网络为模型核心,实现了文本分类的基线模型,同时利用 PyTorch 作为深度学习模型框架的优势,对模型进行了编码。这个模型中使用的数据集取自一个 Kaggle 竞赛。这个数据集是由推文组成的。在预处理步骤中,展示了一种处理文本数据的特殊技术,即标记化。
未来的工作
正如前面提到的,这个博客的目的是为文本分类任务提供一个基线模型。值得一提的是,文本分类的问题超出了两层 LSTM 体系结构,在该体系结构中,文本在基于标记的方法下进行预处理。最近的工作通过实施基于变压器的架构(例如 BERT )展示了令人印象深刻的结果。然而,通过遵循这一思路,可以通过移除基于标记的方法并代之以实现基于单词嵌入的模型(例如, word2vec-gensim )来改进所提出的模型。同样,可以应用双向 LSTMs 来捕捉更多的上下文(以向前和向后的方式)。
完整的代码可在https://github . com/FernandoLpz/Text-Classification-lst ms-py torch获得
随意克隆或者叉:)
Keras 中多输出多损失的文本分类器
使用 Keras 构建多标签分类器似乎不是一项困难的任务,但当你处理一个高度不平衡的数据集时,它有 30 多个不同的标签和多个丢失,这可能会变得非常棘手。
在本帖中,我们将详细介绍多标签分类器的定义、多重损失、文本预处理,并逐步解释如何在 Keras 中构建多输出 RNN-LSTM。
我们将要处理的数据集由自然灾害信息组成,这些信息被分为 36 个不同的类别。数据集由图八提供。输入消息示例:
['Weather update - a cold front from Cuba that could pass over Haiti',
'Is the Hurricane over or is it not over',
'Looking for someone but no name',
'UN reports Leogane 80-90 destroyed. Only Hospital St. Croix functioning. Needs supplies desperately.',
'says: west side of Haiti, rest of the country today and tonight']
什么是多标签分类问题?
在解释它是什么之前,让我们先来看一个更常见的分类类型的定义:Multiclass。在一个多类中,类是互斥的,也就是说,你一次只能分类一个类。例如,如果您有几个类:{Car,Person,Motorcycle},那么您的模型必须输出:Car 或 Person 或 Motorcycle。对于这类问题,使用 Softmax 函数进行分类:
神经网络中的 Softmax 分类函数
对于多标签分类,一个数据样本可以属于多个类别。从上面的例子中,您的模型可以对同一个样本进行分类:Car 和 Person(假设每个样本是一个可能包含这 3 个类的图像)。
在所研究的数据集中,有 36 个不同的类,其中 35 个有一个二进制输出 : 0 或 1;并且其中 1 个具有 3 个可能的类别(多类别情况): 0、1 或 2。
多重损失
在同一个模型中使用多个损失函数意味着您正在执行不同的任务,并在这些任务之间共享模型的一部分。有时,你可能会想,也许为每种不同类型的输出建立不同的模型更好,但在某些情况下,共享你的神经网络的某些层有助于模型更好地概括。
Keras 如何处理多次亏损?
从 Keras 文档、“…模型将最小化的损失值将是所有单个损失的加权和,由 *loss_weights*
系数加权。”。因此,最终损失是每个损失的加权和,传递给*loss*
参数。
在所研究的案例中,将使用两种不同的损失:
- 对于二进制类,使用的度量将是具有相应
binary_crossentropy
损失的binary_accuracy
。由于每个输出只有两个可能的类别(0 或 1),因此sigmoid
功能将被用作激活功能。 - 对于多类输出,,使用的度量将是具有相应
sparse_categorical_crossentropy
损耗的sparse_categorical_accuracy
。对于该输出,有 3 个可能的类别:0、1 和 2,这样将使用softmax
激活功能。与传统的categorical_crossentropy
损耗不同,第一种损耗不需要对输出 Y 进行一键编码。因此,不用将输出转换为:[1,0,0],[0,1,0]和[0,0,1],我们可以将其保留为整数:[0],[1]和[2]。需要强调的是,这两种损失属于同一个等式:
类别交叉熵和稀疏类别交叉熵的损失函数
文本预处理
就像任何其他 NLP 问题一样,在将文本输入数据应用到模型中之前,我们必须对其进行预处理。在这个数据集中,标点符号,网址链接和“@”被删除。尽管“@”提到给消息增加了一些信息,但它没有给分类模型增加价值。标签(' #') 可能包含有用的信息,因为它们通常与事件相关。因此,它们被保留下来,只删除了“#”字符。
停用词(一种语言中最常见的词)移除和词条化也应用于数据集。
文本样本被格式化为张量,可以使用 Keras 实用程序将其输入神经网络:
上述过程主要包括 3 个步骤:
- 首先,将记号赋予器实例(T7)适配到语料库,基于词频创建词汇索引。每个单词都映射到一个索引,所以每个单词都有一个唯一的整数值,整数越小,单词出现的频率越高。要保留的单词大小由
num_words
参数定义,即词汇量。只会保留最常用的词。在我们的数据集中,单词映射如下:
print(tokenizer.word_index){'water': 1, 'people': 2, 'food': 3, 'need': 4, 'help': 5, 'please': 6, 'earthquake': 7, 'would': 8, 'area': 9, 'like': 10, 'said': 11, 'country': 12,...}
- 然后使用
tokenizer.texts_to_sequences
方法将输入的句子映射成整数。从我们的例子来看:
'weather update cold front cuba could pa haiti'
映射到:
[138, 1480, 335, 863, 2709, 80, 411, 18]
- 最后,为了创建嵌入,我们所有的句子需要长度相同。因此,我们使用
pad_sequences
给每个句子填充零。
多输出多损耗 RNN
为了构建这个模型,我们将使用 Keras functional API ,而不是 Sequential API ,因为前者允许我们构建更复杂的模型,例如多输出和输入问题。
为了总结我们到目前为止的成果:
- 每个输入样本是 MAXLEN (50)大小的整数向量
- 每个样本将被分为 36 个不同的类别,其中 35 个类别具有二进制输出 : 0 或 1;并且其中 1 个具有 3 个可能的类别(多类别情况): 0、1 或 2。
模型架构
在本节中,我们将使用 Keras 嵌入层来训练我们自己的嵌入。
嵌入层将以下内容作为输入:
- input_dim :我们选择的词汇量
- output_dim :嵌入的大小。在我们的例子中,它被设置为 50d。
- 输入长度:输入序列的长度。在我们的例子中:MAXLEN
为了加快训练时间,在 LSTM 之前增加了卷积层。CNN 更有可能从句子中提取局部和深层特征。你可以在这篇文章中读到更多关于 CNN 和 RNN 组合的内容。
输出层
大多数输出类都是二进制的,但其中一个是多类输出。如“多重损耗”部分所述,所用损耗为:binary_crossentropy
和sparse_categorical_crossentropy
。
由于数据集高度不平衡,添加了class_weight
参数以减少不平衡分布。将为每个输出创建一个密集层。输出将存储在一个数组中,而每个输出的度量和损失将存储在相应的字典中。
上面的代码遍历每个输出二进制列并创建一个密集层,将相应的度量和损失保存到一个字典中。
下面的代码对单个多类输出列应用相同的过程。
对于每个输出,我们以字典格式定义每个类的权重:
然后,我们可以实例化一个模型类并训练我们的模型:
该模型遵循以下格式:
RNN 的图形表示
显然,对于多标签输出,Keras 有一个未解决的问题与class_weights
和binary_crossentropy
。上面提出的解决方案(为每个输出添加一个密集图层)是一个有效的解决方案。
结论
在这篇文章中,我们使用 Keras functional API 构建了一个 RNN 文本分类器,具有多个输出和丢失。我们浏览了关于多重损失的解释,以及多标签和多类别分类问题之间的区别。
你可以在这里查看完整的代码和额外的分析!在这篇文章中,我们训练了我们自己的嵌入,在 GitHub Repo 中,你可以检查使用预训练手套向量重新训练的相同模型。
参考
使用 Keras 进行多标签文本分类的 CNN 和 LSTMs 体系结构
medium.com](https://medium.com/towards-artificial-intelligence/keras-for-multi-label-text-classification-86d194311d0e) [## 机器学习中如何定义多重损失?
begingroup$使用两个损失意味着您对优化这两个损失都感兴趣。它可能来自于你是…
stats.stackexchange.com](https://stats.stackexchange.com/questions/323961/how-to-define-multiple-losses-in-machine-learning) [## keras 如何处理多次亏损?
来自模型文档:loss: String(目标函数的名称)或目标函数。参见损失。如果模型有…
stackoverflow.com](https://stackoverflow.com/questions/49404309/how-does-keras-handle-multiple-losses) [## 多热点稀疏分类交叉熵
在进行多类分类时,会大量使用类别交叉熵损失。它比较预测的标签…
cwiki.apache.org](https://cwiki.apache.org/confluence/display/MXNET/Multi-hot+Sparse+Categorical+Cross-entropy) [## Keras 嵌入层的详细说明
使用 Kaggle 笔记本探索和运行机器学习代码|使用来自多个数据源的数据
www.kaggle.com](https://www.kaggle.com/rajmehra03/a-detailed-explanation-of-keras-embedding-layer)
自然语言处理中的文本清洗方法
保罗·花冈在 Unsplash 上的照片
为 NLP 准备数据的 5 种 python 方法
自然语言处理被定义为“将计算技术应用于自然语言和语音的分析和合成”。为了执行这些计算任务,我们首先需要将文本语言转换为机器可以理解的语言。
在下面的帖子中,我将描述为自然语言处理准备文本数据的最常见步骤。我将讨论一些可用于执行这些步骤的工具,并提供一些示例 python 代码来执行它们。
在整篇文章中,我将使用一个取自 Kaggle 的数据集,该数据集可以从这里下载。这个数据集由来自推文的文本和一个目标变量组成,该变量将推文分类为真正的灾难和非灾难。下面的代码使用 pandas 将数据读入数据框。为了简单起见,我删除了除文本和目标变量之外的所有列。
import pandas as pd
pd.set_option('display.max_colwidth', -1)train_data = pd.read_csv('train.csv')
cols_to_drop = ['id', 'keyword', 'location']
train_data = train_data.drop(cols_to_drop, axis=1)
train_data.head()
1)标准化
处理语言数据的关键步骤之一是去除噪声,以便机器可以更容易地检测数据中的模式。文本数据包含大量干扰,这些干扰以特殊字符的形式出现,如标签、标点符号和数字。所有这些如果出现在数据中,计算机都很难理解。因此,我们需要处理数据以去除这些元素。
此外,注意单词的大小写也很重要。如果我们包含相同单词的大写和小写版本,那么计算机会将它们视为不同的实体,即使它们可能是相同的。
下面的代码执行这些步骤。为了跟踪我们对文本所做的更改,我将干净的文本放在了一个新的列中。输出显示在代码下方。
import redef clean_text(df, text_field, new_text_field_name):
df[new_text_field_name] = df[text_field].str.lower()
df[new_text_field_name] = df[new_text_field_name].apply(lambda elem: re.sub(r"(@[A-Za-z0-9]+)|([^0-9A-Za-z \t])|(\w+:\/\/\S+)|^rt|http.+?", "", elem))
# remove numbers
df[new_text_field_name] = df[new_text_field_name].apply(lambda elem: re.sub(r"\d+", "", elem))
return dfdata_clean = clean_text(train_data, 'text', 'text_clean')data_clean.head()
2)停用词
停用字词是常见的字词,对于某些计算过程来说,这些字词提供的信息很少,或者在某些情况下会引入不必要的噪声,因此需要将其移除。对于文本分类任务来说尤其如此。
在其他情况下,不建议删除停用词,或者需要更仔细地考虑。这包括由于删除停用词而失去一段文本意义的任何情况。例如,如果我们正在构建一个聊天机器人,并从短语“我不高兴”中删除单词“而不是,那么算法实际上可能会解释相反的意思。这对于聊天机器人或情感分析等用例尤为重要。
自然语言工具包 (NLTK) python 库内置了移除停用词的方法。下面的代码使用它来删除 tweets 中的停用词。
import nltk.corpus
nltk.download('stopwords')from nltk.corpus import stopwords
stop = stopwords.words('english')data_clean['text_clean'] = data_clean['text_clean'].apply(lambda x: ' '.join([word for word in x.split() if word not in (stop)]))
data_clean.head()
3)词干
词干化是将单词还原为其词根形式的过程。比如“ rain ”、“ raining ”和“ rained ”这几个词就非常相似,在很多情况下,意思是一样的。堵塞的过程会将这些还原为“雨”的根形式。这也是一种减少噪音和数据维数的方法。
NLTK 库也有执行词干提取任务的方法。下面的代码使用 PorterStemmer 来阻止我上面的例子中的单词。从输出中可以看到,所有的单词现在都变成了“rain”。
from nltk.stem import PorterStemmer
from nltk.tokenize import word_tokenizeword_list = ['rains', 'raining', 'rain', 'rained']ps = PorterStemmer()
for w in word_list:
print(ps.stem(w))
在我们对数据进行词干提取之前,我们需要对推文进行标记。这是一种用于将文本拆分成其组成部分(通常是单词)的方法。下面的代码使用 NLTK 来完成这项工作。我已经将输出放到了一个名为“text_tokens”的新列中。
import nltk
nltk.download('punkt')from nltk.tokenize import sent_tokenize, word_tokenizedata_clean['text_tokens'] = data_clean['text_clean'].apply(lambda x: word_tokenize(x))data_clean.head()
下面的代码使用 NLTK 中的 PorterStemmer 方法对 text_tokens 应用词干,并将处理后的文本输出到新列。
def word_stemmer(text):
stem_text = [PorterStemmer().stem(i) for i in text]
return stem_textdata_clean['text_tokens_stem'] = data_clean['text_tokens'].apply(lambda x: word_stemmer(x))
data_clean.head()
4)词汇化
词汇化的目标与词干化的目标是一样的,因为它旨在将单词简化为它们的根形式。然而,众所周知,词干处理是一种相当粗糙的方法。另一方面,词汇化是一种工具,它执行完整的词法分析,以更准确地找到单词的词根或“词汇”。
同样,NLTK 可以用来执行这项任务。
nltk.download('wordnet')
from nltk.stem import WordNetLemmatizerdef word_lemmatizer(text):
lem_text = [WordNetLemmatizer().lemmatize(i) for i in text]
return lem_textdata_clean['text_tokens_lemma'] = data_clean['text_tokens'].apply(lambda x: word_lemmatizer(x))
data_clean.head()
5)词性标注和组块
词性标注是一种对单词进行分类的方法,它提供了一些与单词在语音中的使用方式有关的信息。
有八个主要的词类,它们都有相应的标签。这些显示在下表中。
NLTK 库有一个执行词性标注的方法。下面的代码对我们数据集中的 tweets 执行 POS 标记,并返回一个新列。
def word_pos_tagger(text):
pos_tagged_text = nltk.pos_tag(text)
return pos_tagged_textnltk.download('averaged_perceptron_tagger')data_clean['text_tokens_pos_tagged'] = data_clean['text_tokens'].apply(lambda x: word_pos_tagger(x))
data_clean.head()
组块建立在词性标注的基础上,它使用词性标注中的信息从文本中提取有意义的短语。在许多类型的文本中,如果我们把一切都简化为单个单词,我们可能会失去很多意义。例如,在我们的推文中,我们有很多位置名称和其他短语,这些都是很重要的。
如果我们拿这句话“la ronge sask 附近的森林大火”这个地名“ la ronge ”和“森林大火”这几个字就传达了一个我们可能不想失去的重要意义。
spaCy python 库对此有一个方法。如果我们把这种方法应用到上面的句子中,我们可以看到它分离出了合适的短语。
import spacy
nlp = spacy.load('en')text = nlp("forest fire near la ronge sask canada")
for chunk in text.noun_chunks:
print(chunk.text, chunk.label_, chunk.root.text)
本文给出了一些最流行的方法的简要概述,这些方法用于清理准备用于自然语言处理的文本数据。为了更深入地了解这些概念和工具,这篇博客 nlpforhackers.io 是我的最爱之一。
如果你对将机器学习技术应用于这些数据感兴趣,我上周写了一篇文章,介绍如何让你的第一次提交给这个数据集来自的 Kaggle 竞赛。
感谢阅读!
文本数据扩充
自然语言处理
用于文本数据扩充的 TextAttack 和 Googletrans 简介
Data Augmentation
是使我们能够增加训练数据的大小而无需实际收集数据的过程。但是为什么我们需要更多的数据呢?答案很简单——我们拥有的数据越多,模型的性能就越好。
图像数据增加步骤,例如翻转、裁剪、旋转、模糊、缩放等。对计算机视觉有很大的帮助。此外,创建增强图像相对容易,但由于语言固有的复杂性,Natural Language Processing (NLP)
就不一样了。(例如,我们不能用同义词替换每个单词,即使我们替换了它,句子的意思也可能完全改变)。根据我的发现/研究,我还没有看到像图像增强那样多的围绕文本数据增强的研究。
然而,在这篇文章中,我们将浏览两个库TextAttack
& Googletrans
,这是我最近在尝试扩充文本数据时遇到的。那么,我们开始吧。
我们将应用我们将要学习的增强技术来引用西蒙·西内克的话。
“领导力需要两样东西:对尚不存在的世界的愿景,以及传达这种愿景的能力。”
“领导者的角色不是提出所有伟大的想法。领导者的角色是创造一个可以产生伟大想法的环境”
西蒙·西内克
文本攻击
[TextAttack](https://github.com/QData/TextAttack)
是一个 Python 框架,用于 NLP 中的对抗性攻击、对抗性训练和数据扩充。在本文中,我们将只关注数据扩充。
安装
!pip install textattack
用途
textattack.Augmenter
类提供了六种数据扩充方法。你可以在官方文档中找到这些方法的详细描述。
- wordnet 增强器
- 嵌入增强器
- CharSwapAugmenter
- 简易数据增强器
- 检查表增强器
- 克莱尔增强器
让我们看看使用这四种方法的数据增强结果。注意,默认情况下,pct_words_to_swap=0.1
、transformations_per_example=4
被传递给这些方法中的每一个。我们可以根据需要修改这些默认值。
我们可以将这些方法应用于真实世界的数据,以增加数据的大小。下面给出了示例代码。这里,原始的train
数据帧被复制到train_aug
数据帧,然后在train_aug
上应用增强。最后,train_aug
被附加到原始的train
数据集。
train_aug = train.copy()from textattack.augmentation import EmbeddingAugmenter
aug = EmbeddingAugmenter()
train_aug['text'] = train_aug['text'].apply(lambda x: str(aug.augment(x)))
train = train.append(train_copy, ignore_index=True)
Googletrans
Googletrans
建立在谷歌翻译 API 之上。这使用 Google Translate Ajax API 进行语言检测和翻译。
安装
!pip install googletrans
使用
translate()
方法的关键参数是:
src
:源语言。可选参数如googletrans
将检测它。
dest
:目的语言。强制参数。
text
:要从源语言翻译成目标语言的文本。强制参数。
我们可以看到,给定的文本首先从English
翻译到Italian
,然后再翻译回English
。在这个反向翻译中,我们可以看到,原文和回译文本之间的句子有轻微的变化,但句子的整体意思仍然保留。
我们可以将这种技术应用于真实世界的数据。下面给出了示例代码。这里,原始的train
数据帧被复制到tran_aug
数据帧,然后back-translation
被应用到train_aug
数据帧。最后,train_aug
被附加到原始的train
数据帧上。请注意,我们将原文从英语翻译成意大利语,然后从意大利语翻译成英语。
train_aug = train.copy()
from googletrans import Translator
translator = Translator()
train_aug['text'] = train_aug['text'].apply(lambda x: translator.translate(translator.translate(x, dest='it').text, dest='en').text)
train = train.append(train_aug, ignore_index=True)
结论
现在您知道了如何在您的数据科学项目中使用TextAttack
和Googletrans
库来扩充文本数据。
阅读更多关于 Python 和数据科学的此类有趣文章, 订阅 到我的博客【www.pythonsimplified.com】。 你也可以通过LinkedIn联系我。
参考
Googletrans 是一个免费且无限制的 python 库,实现了 Google Translate API。这使用谷歌…
github.com](https://github.com/ssut/py-googletrans) [## QData/TextAttack
为 NLP 模型生成对抗示例[text attack docs on ReadTheDocs]关于设置使用*设计…
github.com](https://github.com/QData/TextAttack) [## 自然语言处理中的数据扩充
文本增强简介
towardsdatascience.com](/data-augmentation-in-nlp-2801a34dfc28)
基于图转换器的知识图文本生成
结构概要
鉴于变压器模型开发的速度和 GPT-3 改变世界的影响,这篇 2019 年的论文有点不合时宜。然而,作为一个有认知科学背景的人,我喜欢模型,因为它们的结构而不是原始的计算能力,可以帮助揭开认知过程的帷幕。该模型试图解决表示长期依赖关系的问题。
语言生成大致包括两个部分,规划和实现。实现纯粹是生成语法文本的任务,不管它的意义、与其他文本的关系或整体意义。规划是确保文本中实体之间的长期依赖关系得到解决,以及项目以正确的方式在语义上相互关联的过程。在下一个短语中,关键实体是约翰,伦敦,英国,酒保。
Moryossef,2019 |【https://arxiv.org/abs/1904.03396
规划是管理那些实体和它们之间的关系,实现是产生短语“约翰,他是一名酒保,住在英国的首都伦敦”(Moryossef,2019)。
GraphWriter 根据标题中的单词和构建的知识图生成摘要。本文的贡献包括:
- 一种新的图形转换器编码器,将序列转换器应用于图形结构输入
- 展示了如何将 IE 输出转换成一个连通的无标签图,以用于基于注意力的编码器
- 与科学文本配对的知识图表数据集,用于进一步研究
在输入进入编码器之前(稍后会详细介绍),必须以正确的方式进行排列。这个模型的输入有两个渠道,标题,以及实体和关系的知识图。
资料组
为此,引入了议程数据集——基于取自语义学者语料库的前 12 次人工智能会议的 4 万篇论文标题和摘要。(Ammar 等人,2018 年)在下面的图表创建和预处理之后,数据集被输入到模型中。完整的回购,包括数据集,可在这里获得。
图形预处理:
创建知识图表:
- 应用了栾等人(2017)的 NER/IE SciIE 系统,该系统提取实体并对它们进行标记,以及创建共指标注
- 然后,这些注释被折叠成单个带标签的边
Koncel-Kedziorski(艾伦研究所)| 2019
3.然后,使用添加的全局节点将每个图转换为连通图,所有其他节点都连接到该全局节点。
4.然后,每个带标签的边被表示每个方向上的关系的两个节点所代替,并且新的边是不带标签的。这允许图形被表示为邻接矩阵,这是容易处理的必要前提。
Koncel-Kedziorski(艾伦研究所)| 2019
这个图形成的一个关键特征是增加了一个全局节点 G,所有的实体都连接到这个节点上,这个节点把不连通的图变成了连通的图。因此,最终产品是一个连通的、无标号的二分图。
模型架构:
该模型使用编码器-解码器架构,以未标记的二分图和标题作为输入。
Koncel-Kedziorski(艾伦研究所)| 2019
标题编码器:标题用 BiLSTM 编码,使用 512 维嵌入。没有预先训练的单词嵌入被用于此。
图形编码器:
第一步是,通过关注 v 的邻域中的所有其他顶点,每个顶点表示 v i 被上下文化。
然后,图形编码器通过连接由 N 个注意力头部产生的注意力权重的乘积来创建一组顶点嵌入。
然后用“块”网络增加这些嵌入,所述“块”网络由每个两层的多个堆叠块组成,具有以下形式:
最终结果是一个实体、关系及其与全局节点的上下文的列表,称为图上下文化顶点编码。
解码器(图形和标题)
解码器是基于注意力的,具有复制机制,用于从知识图中的实体和标题中的单词复制输入。它还在每个时间步长 t 使用隐藏状态 h t。
根据用编码器创建的编码,使用多头注意力来计算上下文向量 Cg 和 Cs
这里,Cs(标题上下文向量)的计算方式与 Cg 相同。这两者连接在一起形成 Ct,即总的上下文向量。
根据该上下文向量,从标题中的单词或实体名称复制标记的概率是通过总上下文向量乘以 Wcopy 来计算的。
最终的下一个令牌概率分布是:
实验
GraphWriter 与 GAT(用图形注意力替换了图形转换器)、EntityWriter(类似 GraphWriter,但不使用图形结构)、Rewriter (Wang 等人,2018)(仅使用标题编码)进行比较。
使用的人工和自动评估指标:
- 领域专家判断摘要是否符合标题。最佳-最差标度(Louviere 和 Woodworth,1991 年)
- 蓝色(帕皮尼尼等人,2002 年)
- METEOR (Denkowski 和 Lavie,2014 年)(生成的输出与原始摘要相比,在单字频率上的精确度和召回率)
自动评估:
人类评价:
这篇论文有一个特别的地方让我有点失望;“主题专家”是计算机专业的本科生。虽然本科生毕业时可以知道很多东西,但我不知道他们通常对计算机科学的学术论文有多熟悉。
关于人类对抽象质量的判断,我认为最能说明问题的是,虽然人类的产出并不总是被认为是最好的,但也从来不是最差的。这让我怀疑,这些生成的摘要中有一些相对缺乏经验的本科生不太清楚的东西,但尽管如此,还是提醒他们有些不对劲。
在研究了用于生成的解码机制之后,问题是最终有多少被复制了,有多少被生成了?为此,我给作者 Rik Koncel-Kedziorski 发了电子邮件,要他提供的摘要和原始文档的副本。
原文摘要:
we present a learning architecture for lexical semantic classification problems that supplements task-specific training data with background data encoding general '' world knowledge '' . the learning architecture compiles knowledge contained in a dictionary-ontology into additional training data , and integrates task-specific and background data through a novel hierarchical learning architecture . experiments on a word sense disambiguation task provide empirical evidence that this '' hierarchical learning architecture '' outperforms a state-of-the-art standard '' flat '' one .
graph writer 生成的摘要:
in this paper , we present a novel learning architecture for lexical semantic classification problems , in which a learning architecture can be trained on a large amount of training data . in the proposed learning architecture , a hierarchical learning architecture is learned to select the most informative examples from the source domain to the target domain . the learning architecture is trained in a hierarchical learning architecture , where the background data is learned from the training data in the target domain . the learning architecture is trained on data from the source domain and a target domain in the target domain . experimental results show that the proposed learning architecture is effective in improving the performance of lexical semantic classification problems .
分析:
这里调查的一个关键问题是知识是否有帮助,以某种显式编码的形式,而不是仅仅被一个有许多参数的模型吸收。在这种情况下,它做到了。Rewriter 是一家总部位于 LSTM 的模型,它只使用标题词来生成摘要,在所有选择的评估指标中表现最差。EntityWriter 比 GraphWriter 使用更多的信息,是一个有效的控件,因为它使用提取的相同实体,但没有图形提供的上下文。它比没有使用任何形式的知识表现更好,但仍然优于使用由图创建的上下文的模型。
我认为在这里不要认为 GraphWriter 与 GPT-3 在合理的人类输出方面存在直接竞争是很重要的,它显然不是,我非科学地调查过的那些从来没有过的专家也不难分辨哪个是人类产生的,哪个显然不是。但是,这不是测试,也不是目标。测试是使用结构化领域知识的系统是否会比不使用结构化领域知识的系统做得更好,比如 EntityWriter,在这种情况下,确实如此。
参考资料:
这个库包含了我们的论文《用图形转换器从知识图生成文本》的源代码
github.com](https://github.com/rikdz/GraphWriter)
Amit Moryossef、Yoav Goldberg 和 Ido Dagan。2019.一步一步:在神经数据到文本的生成中将计划与实现分开。计算语言学协会北美分会 2019 年会议录:人类语言技术,第 1 卷(长论文和短论文),第 2267-2277 页,明尼苏达州明尼阿波利斯。计算语言学协会
莫里塞夫,戈德堡,达甘,https://arxiv.org/abs/1904.03396
孔塞尔-凯济奥尔斯基,https://arxiv.org/abs/1904.02342
使用 N 元模型的文本生成
迪伦·卢在 Unsplash 上的照片
当我了解到机器能够通过使用一些简单的统计和概率技术来生成新文本时,我对人工智能,特别是自然语言处理(NLP)的兴趣就爆发了。在这篇文章中,我想分享这个非常简单和直观的创建文本生成模型的方法。
背景
众所周知,语言具有顺序性,因此单词在文本中出现的顺序非常重要。这个特性允许我们理解一个句子的上下文,即使缺少一些单词(或者偶然发现一个意思未知的单词)。考虑下面的例子:
"玛丽被卓帕卡布拉发出的可怕声音吓坏了."
没有一些先前的背景,我们不知道“卓帕卡布拉”到底是什么,但我们可能会说,我们不会高兴在现实生活中遇到这种生物。
句子中单词的这种依赖性可以给我们一些关于丢失单词性质的线索,有时我们甚至不需要知道整个上下文。在上面的例子中,通过只看由发出的噪声,我们在直觉层面上可以说下面的单词应该是一个名词而不是某个其他词类。
这让我们想到了 N 元语法背后的概念,它的正式定义是“来自给定文本样本的 N 个项目的连续序列”。主要的想法是,给定任何文本,我们可以把它分成一个一元(1-gram)、二元(2-gram)、三元(3-gram)等的列表。
例如:
文字:“我去跑步了”
单字:[(I),(got),(running)]
二元模型:[(我,去,(去,运行)]
正如你所注意到的,单词“go”出现在两个二元模型中:(I,go)和(go,running)。
另一种更直观的方式来看待它
句子中三元组的例子。图片由奥列格·鲍里索夫拍摄。
理论
使用 n-gram 生成文本的主要思想是假设 n-gram 的最后一个单词 (x^{n})可以从同一个 n-gram 中出现的其他单词(x{n-1},x,… x)推断出来,我称之为上下文。
因此,该模型的主要简化是,我们不需要跟踪整个句子来预测下一个单词,我们只需要回头寻找 n-1 标记。这意味着主要的假设是:
太好了!最美妙的是,为了计算上面的概率,我们只需要应用简单的条件概率规则
例如:使用三元模型(n=3)
正文:“玛丽害怕是因为 __”
因为我们使用三元模型,所以我们删除了句子的开头:“Mary 被吓到了”,并且我们只需要计算“因为”的可能延续。假设从数据集我们知道有以下可能的延续:“我”,“噪音”。因此,我们需要计算:
P(噪音|因为)和 P(我|因为)
在计算出概率后,有多种方法来选择给定所有候选的最终单词。一种方法是产生具有最高条件概率的单词(如果 n 很小,这个选项可能不是最好的,因为它会陷入循环)。
另一个(也是更好的)选项是根据条件概率半随机地输出单词“”。从而具有较高概率的单词将有较高的机会被产生,而具有较低概率的其他单词有机会被产生。
密码
理论到此为止,让我们进入实现部分。我的 github 上有源代码。
首先,我们需要一些源文本,从中我们将训练我们的语言模型。理想情况下,我们希望有一些大的书(甚至多本书),因为我们不仅希望有大量的词汇,而且我们有兴趣看到尽可能多的不同排列或单词。这里我将使用玛丽·雪莱写的《弗兰肯斯坦》,但是你可以使用你选择的任何其他书(为了方便你可以使用古腾堡计划链接)。
我希望代码尽可能简单,这样就不需要安装任何外部包。除此之外,我还做了一些代码优化,以减少计算时间,使有效的推理过程。
在我们实现 N 元语法语言模型之前,让我们实现一些辅助函数:一个用于执行标记化(拆分句子中的单词),另一个用于将结果标记合并到 N 元语法中。
对于标记化,最好使用一些外部库,如 NLTK 或 spaCy,但是对于我们的目的,自定义标记化器就足够了。
现在我们来看看get_ngrams
函数。
正如我们之前提到的,为了预测位置 n 的标记,我们必须查看我们的 N-gram 的所有先前的 n-1 标记。所以我们要用一个类型为((context), current_word)
的元组来表示我们的 N-gram:
当我们需要生成一个句子的第一个标记时,问题就出现了,因为没有前面的上下文可用。为了解决这个问题,我们可以引入一些像<START>
这样的前导标签,这将确保在任何推断点上,我们总是使用正确的 N 元语法。例如,如果我们想从文本“我买了一辆红色汽车”中获取所有的 3 克:
[(('
',' '),'我'), (('
','我','买'), (('我','买','甲'),
(('买了',' a ','红'),
(('甲','红','车'),
很好,现在让我们创建我们的 N 元模型:
在初始化中,我们必须指定,什么是我们的 n 元文法的 n 值,除此之外,我还创建了上下文和 ngram_counter 字典。字典作为键,它有上下文,作为值,它存储给定上下文的可能延续的列表。例如:
*>> self.context
>> {(red, car): [names, wallpapers, game, paint ...],
(weather, in): [London, Bern, Paris, ...]}*
因此,在某种程度上,这本上下文词典立即向我们展示了我们可以使用哪些候选词来生成相对随机的、有一定意义的文本(仍然比盲目生成一堆单词要好)。
字典只是计算一个特定的 N-gram 在我们的训练集中出现过多少次。
为了更新我们的语言模型,我们将提供书中的每个单独的句子,并将使用与我们的 N 元语法相对应的信息来更新字典。
正如在本文的理论部分提到的,在给定上下文的情况下,要计算下一个单词的概率,我们只需要应用简单的条件概率规则。
现在让我们转到文本生成部分。
在我们开始文本生成之前,我们首先必须为我们的系统提供一些上下文,在我们的例子中,它将是重复n–1次的开始标记<START>
。
之后,我们可以通过使用我们的“半随机”方法生成我们的第一个random_token
。然后,我们重复我们的过程,直到生成了一定数量的令牌(由token_count
变量指定)。
注意:当我们到达生成中的句号.
时,由于我们更新语言模型的方式,系统将不知道如何继续。因此,每次我们的模型生成一个句号时,我们都需要重新初始化上下文队列context_queue
。
太好了!一切都设置好了,让我们运行我们的模型,看看我们能产生什么句子!
部分结果如下:
n=1,“我的关心。这是命中注定的。。对于费利克斯的话,安不哪措激动”
n=2,“我同情他,多么沉重;但我已经到达,但他们断言,我不允许潮湿和”
n=3,”我继续他们的单个后代。最后,我从一个生于自由的人那里得知
n=4,”我继续以这种方式走着,在此期间,我享受着幸福的感觉。你仍能聆听”
n=5,“我继续以这种方式走了一段时间,我担心德蒙的失望的影响。”
看到生成的文本如何随着我们增加 n 而改进是很酷的,然而这并不奇怪,因为随着 N 元模型大小的增加,我们开始从训练集复制原始文本,在我们的例子中是 book。
事实上,对于 n > 5 生成的文本仍然是“我继续以这种方式行走了一段时间,试图通过身体锻炼来减轻负重”。
结束语
我仍然感到印象深刻的是,我们使用 N 元语法和简单概率规则的过于简化的语言模型仍然能够产生一些新的文本,对读者来说有一定的意义。
当然,这样一个天真的模型不可能写出像人类一样的文章,也不可能像 GPT 3 号那样表演,因为它有很多缺点。这种系统可能陷入循环,或者不能跟踪长期的上下文关系。但这是对自然语言处理和语言建模的第一次很好的介绍,因为它强调了句子中上下文的重要性。
敬请关注更多关于人工智能和自然语言处理的文章。
用 PyTorch 中的双 LSTM 生成文本
通过使用 PyTorch 的 LSTMCells 从头开始创建双 LSTM 模型来构建文本生成模型的分步指南
帕特里克·托马索在 Unsplash 上的照片
“没有规定怎么写。有时它来得容易而完美:有时它就像钻岩石,然后用炸丨药把它炸出来”——欧内斯特·海明威
这篇博客的目的是解释如何通过实现一个基于 LSTMs 的强大架构来构建一个用于文本生成的端到端模型。
博客分为以下几个部分:
- 简介
- 文本预处理
- 序列生成
- 模型架构
- 培训阶段
- 文本生成
您可以在以下网址找到完整的代码:https://github . com/FernandoLpz/Text-Generation-BiLSTM-py torch
介绍
这些年来,各种各样的提案被推出来模拟自然语言,但是这是怎么回事呢?“建模自然语言的想法指的是什么?我们可以认为“建模自然语言”指的是对组成语言的语义和语法进行推理,本质上是这样,但它走得更远。
如今,自然语言处理 ( NLP )领域通过不同的方法和技术处理涉及语言的推理、理解和建模的不同任务。NLP(自然语言处理)领域在过去的十年中发展非常迅速。许多模型从不同的角度提出了不同的 NLP 任务。同样,最受欢迎的提议中的共同点是实现基于深度学习的模型。
正如已经提到的, NLP 领域解决了大量的问题,特别是在这篇博客中,我们将通过利用基于深度学习的模型来解决文本生成的问题,例如递归神经网络 LSTM 和双 LSTM 。同样,我们将使用当今最复杂的框架之一来开发深度学习模型,具体来说,我们将使用来自 PyTorch 的LSTMCell类来开发所提议的架构。
如果你想深入了解 LSTM 的机制,以及它是如何在 PyTorch 中实现的,看一看这个令人惊叹的解释: 从 LSTM 细胞到带有 PyTorch 的多层 LSTM 网络
问题陈述
给定一个文本,神经网络将通过字符序列来学习给定文本的语义和语法。随后,将随机抽取一个字符序列,并预测下一个字符。
所以,让我们开始吧!
文本预处理
首先,我们需要一个我们将要使用的文本。有不同的资源在那里你可以找到不同的纯文本文本,我推荐你看一看 古腾堡项目 。
在这种情况下,我将使用由乔治·伯德·格林内尔所著的《印第安人中的杰克》这本书,你可以在这里找到这本书: 链接到本书 。所以,第一章的第一行看起来像:
The train rushed down the hill, with a long shrieking whistle, and then began to go more and more slowly. Thomas had brushed Jack off and thanked him for the coin that he put in his hand, and with the bag in one hand and the stool in the other now went out onto the platform and down the steps, Jack closely following.
如您所见,文本包含大写、小写、换行符、标点符号等。建议做的是尝试将文本改编为一种形式,这种形式允许我们以更好的方式处理它,并且主要降低我们将要开发的模型的复杂性。所以我们将把每个字符转换成小写形式。同样,最好将文本作为一个字符列表来处理,也就是说,我们将拥有一个字符列表,而不是一个“大字符串”。将文本作为一个字符序列的目的是为了在生成序列时更好地处理模型(我们将在下一节中详细讨论)。
所以让我们开始吧!
代码片段 1。预处理
正如我们所看到的,在第 2 行中,我们定义了要使用的字符,所有其他符号都将被丢弃,我们只保留“空白字符”符号。在第 6 行和第 10 行,我们正在读取原始文件,并将其转换成小写形式。在第 14 行和第 19 行的循环中,我们创建了一个表示整本书的字符串,并生成了一个字符列表。在第 23 行,我们通过只保留第 2 行定义的字母来过滤文本列表。
因此,一旦文本被加载和预处理,我们将从这样的文本:
text = "The train rushed down the hill."
有这样一个字符列表:
text = ['t','h','e',' ','t','r','a','i','n',' ','r','u','s','h','e','d',' ','d','o','w','n',
' ','t','h','e',' ','h','i','l','l']
我们已经有了完整的文本,作为一个字符列表。众所周知,我们不能将原始字符直接引入神经网络,我们需要一个数字表示,因此,我们需要将每个字符转换成一个数字表示。为此,我们将创建一个字典来帮助我们保存等价的"字符索引"和" i 索引字符"。
所以,让我们开始吧!
代码片段 2。词典创作
我们可以注意到,在第 11 行和第 12 行创建了" char-index "和" index-char "字典。
到目前为止,我们已经展示了如何加载文本并以字符列表的形式保存它,我们还创建了几个字典来帮助我们对每个字符进行编码解码。现在,是时候看看我们将如何生成将被引入模型的序列了。那么,让我们进入下一部分吧!
序列生成
序列生成的方式完全取决于我们将要实现的模型的类型。如前所述,我们将使用 LSTM 类型的递归神经网络,它顺序接收数据(时间步长)。
对于我们的模型,我们需要形成给定长度的序列,我们称之为“ 窗口 ”,其中要预测的字符( 目标 )将是 窗口 旁边的字符。每个序列将由 窗口 中包含的字符组成。为了形成一个序列, 窗口 被一次向右切割一个字符。要预测的字符将始终是跟随 窗口 的字符。我们可以在图 1 中清楚地看到这个过程。
图一。序列生成。在这个例子中,窗口大小为 4,这意味着它将包含 4 个字符。目标是作者的窗口|图像旁边的第一个字符
到目前为止,我们已经看到了如何以简单的方式生成字符序列。现在我们需要将每个字符转换成其各自的数字格式,为此我们将使用预处理阶段生成的字典。这个过程可以在图 2 中看到。
图二。作者将字符转换为数字格式|图片
很好,现在我们知道了如何使用一次滑动一个字符的窗口生成字符序列,以及如何将字符转换成数字格式,下面的代码片段显示了所描述的过程。
代码片段 3。序列生成
太棒了,现在我们知道如何预处理原始文本,如何将它转换成字符列表,以及如何生成数字格式的序列。现在我们进入最有趣的部分,模型架构。
模型架构
正如你已经在这篇博客的标题中读到的,我们将利用双 LSTM 递归神经网络和标准 LSTMs 。本质上,我们利用这种类型的神经网络,因为它在处理序列数据(如文本类型数据)时具有巨大的潜力。同样,也有大量的文章提到了基于递归神经网络的架构的使用(例如 RNN、T10、LSTM、 GRU、、比-LSTM 等)。)进行文本建模,具体为文本生成[1,2]。
所提出的神经网络的架构包括一个嵌入层,接着是一个双 LSTM 以及一个 LSTM 层。紧接着,后者 LSTM 连接到一个线性层上。
方法学
该方法包括将每个字符序列传递到嵌入层,这将为组成该序列的每个元素生成一个矢量形式的表示,因此我们将形成一个嵌入字符的序列。随后,嵌入字符的序列的每个元素将被传递到双 LSTM 层。随后,将产生组成双 LSTM****正向 LSTM 和反向 LSTM 的lstm 的每个输出的级联。紧接着,每个向前+向后连接的向量将被传递到 LSTM 层,从该层中最后一个隐藏状态将被用于馈送到线性层。这个最后的线性层**将具有作为激活函数的 Softmax 函数,以便表示每个字符的概率。图 3 显示了所描述的方法。****
图 3。比尔斯特姆-LSTM 模型。在这张图片中,作者将单词“熊”通过比尔斯特姆-LSTM 模型进行文本生成
太棒了,到目前为止,我们已经解释了文本生成模型的架构以及实现的方法。现在我们需要知道如何用 PyTorch 框架完成所有这些,但是首先,我想简单解释一下 Bi-LSTM 和 LSTM 是如何一起工作的,以便稍后看到我们如何在代码中完成,所以让我们看看 Bi-LSTM 网络是如何工作的。
双 LSTM & LSTM
一辆标准 LSTM 和一辆比 LSTM 的关键区别在于,这辆 比 LSTM 是由 2 个 lstm组成的,更好的说法是“向前【LSTM和“向后* LSTM ”。基本上,前进 LSTM 按原顺序接收,而后退LSTM按相反顺序接收。随后,根据要做的事情,两个lstm的每个时间步的每个隐藏状态可以被加入,或者只有两个lstm的最后状态将被操作。在提议的模型中,我们建议为每个时间步加入两个隐藏状态**。***
太好了,现在我们明白了双 LSTM 和 LSTM 的主要区别。回到我们正在开发的例子,图 4 显示了每个字符序列在通过模型时的演变。
图 4。比尔斯特姆-LSTM 模型。一个简单的例子显示了每个角色在通过作者的模型|图像时的演变
很好,一旦关于 Bi-LSTM 和 LSTM 之间的交互的一切都清楚了,让我们看看如何仅使用来自伟大的 PyTorch 框架的 LSTMCells 在代码中实现这一点。
所以,首先让我们了解一下我们如何构造 TextGenerator 类的构造函数,让我们看看下面的代码片段:
代码片段 4。文本生成器类的构造函数
正如我们所看到的,从第 6 行到第 10 行,我们定义了用于初始化神经网络每一层的参数。需要特别提到的是 input_size 等于词汇表的大小(也就是我们的字典在预处理中生成的包含的元素个数)。同样,要预测的类的数量也与词汇表的大小相同,并且 sequence_length 指的是窗口*的大小。*****
另一方面,在第 20 行和第 21 行,我们定义了两个lstmcell,它们组成了双 LSTM* ( 向前和向后)。在第 24 行中,我们定义了 LSTMCell ,它将被馈送以双 LSTM 的输出。值得一提的是隐藏状态的大小是双 LSTM 的两倍,这是因为双 LSTM 的输出是级联的。稍后在第 27 行我们定义了线性层,稍后将被 softmax 函数过滤。***
一旦定义了构造函数,我们需要为每个 LSTM 创建包含单元格状态* ( cs )和隐藏状态 ( hs )的张量。因此,我们按如下方式进行:***
代码片段 5。权重初始化
太棒了,一旦定义了将包含隐藏状态和单元状态的张量,就该展示整个架构的组装是如何完成的了,让我们开始吧!**
首先,让我们看看下面的代码片段:
代码片段 6。BiLSTM + LSTM +线性层
为了更好的理解,我们将用一些定义的值来解释这个集合,这样我们就可以理解每个张量是如何从一层传递到另一层的。假设我们有:
***batch_size = 64
hidden_size = 128
sequence_len = 100
num_classes = 27***
因此, x 输入张量将有一个形状:
***# torch.Size([batch_size, sequence_len])
x : torch.Size([64, 100])***
然后,在第 2 行中,通过嵌入层传递了 x 张量,因此输出的大小为:
***# torch.Size([batch_size, sequence_len, hidden_size])
x_embedded : torch.Size([64, 100, 128])***
值得注意的是,在第 5 行,我们正在对 x 嵌入张量进行整形。这是因为我们需要将序列长度作为第一维度,本质上是因为在双 LSTM* 中,我们将迭代每个序列,因此整形后的张量将具有一个形状:***
***# torch.Size([sequence_len, batch_size, hidden_size])
x_embedded_reshaped : torch.Size([100, 64, 128])***
紧接着,在第 7 行和第 8 行中定义了向前和向后*列表。在那里我们将存储双 LSTM 的隐藏状态。***
所以是时候给双 LSTM 喂食了。首先,在第 12 行我们迭代了 前进的 LSTM ,我们还保存了每个时间步 ( hs_forward )的隐藏状态。在第 19 行,我们正在迭代向后的LSTM,同时我们正在保存每个时间步* ( hs_backward )的隐藏状态。你可以注意到循环是以同样的顺序进行的,不同的是它是以相反的形式读出的。每个隐藏状态*将具有以下形状:****
****# hs_forward : torch.Size([batch_size, hidden_size])
hs_forward : torch.Size([64, 128])# hs_backward : torch.Size([batch_size, hidden_size])
hs_backward: torch.Size([64, 128])****
太好了,现在让我们看看如何喂养最新的 LSTM 层。为此,我们利用前进和后退列表。在第 26 行中,我们正在遍历每个隐藏状态,对应于第 27 行中连接的向前和向后。值得注意的是,通过连接两个隐藏状态,张量的维数将增加 2X,即张量将具有以下形状:****
****# input_tesor : torch.Size([bathc_size, hidden_size * 2])
input_tensor : torch.Size([64, 256])****
最后,LSTM 将返回一个大小的隐藏状态:
****# last_hidden_state: torch.Size([batch_size, num_classes])
last_hidden_state: torch.Size([64, 27])****
最后, LSTM 的最后隐藏状态将通过 l 线性层,如第 31 行所示。因此,完整的转发函数如下面的代码片段所示:
代码片段 7。正向功能
恭喜你!至此,我们已经知道如何在 PyTorch 中使用 LSTMCell 组装神经网络。现在是时候看看我们如何进行培训了,让我们进入下一部分。
培训阶段
太好了,我们来参加培训。为了执行训练我们需要初始化模型和优化器,稍后我们需要为每个时期和每个小批量进行迭代,让我们开始吧!****
代码片段 8。培训阶段
一旦模型被训练,我们将需要保存神经网络的权重,以便稍后使用它们生成文本。为此我们有两个选择,第一个是定义一个固定数量的历元 s,然后保存权重,第二个是确定一个停止函数以获得模型的最佳版本。在这种特殊情况下,我们将选择第一个选项。在一定数量的时期下训练模型之后,我们如下保存权重:**
代码片段 9。节省重量
完美,到目前为止我们已经看到了如何训练文本生成器以及如何保存权重,现在我们将进入这个博客的顶部,文本生成!那么让我们进入下一部分。
文本生成
太棒了,我们已经到了博客的最后一部分,文本生成。为此,我们需要做两件事:第一件是加载训练好的权重,第二件是从一组序列中随机抽取一个样本作为开始生成下一个字符的模式。所以让我们来看看下面的代码片段:
代码片段 10。文本生成器
因此,通过在以下特征下训练模型:
******window : 100
epochs : 50
hidden_dim : 128
batch_size : 128
learning_rate : 0.001******
我们可以生成以下内容:
******Seed:** *one of the prairie swellswhich gave a little wider view than most of them jack saw quite close to the***Prediction:** *one of the prairie swellswhich gave a little wider view than most of them jack saw quite close to the* wnd banngessejang boffff **we** outheaedd **we band** r hes tller a reacarof t t alethe ngothered uhe th wengaco ack fof ace ca e s alee bin cacotee tharss th **band** fofoutod **we we** ins sange trre anca y w farer **we** sewigalfetwher d e **we** n s shed **pack** wngaingh tthe **we the we** javes t supun f **the** har **man** bllle s ng ou y anghe ond **we** nd ba a **she** t t anthendwe wn **me** anom ly tceaig t i isesw arawns t d ks wao thalac tharr jad d anongive **where** **the** awe w we he is ma mie cack seat sesant sns t imes hethof riges **we he d** ooushe **he hang out** f t thu inong bll llveco **we see** s **the he** haa **is** s igg merin ishe d t san wack owhe o or th we sbe se **we we** inange t ts wan br seyomanthe harntho thengn th me ny **we** ke in acor offff of wan s arghe **we** t angorro **the** wand **be** **thing** a sth t tha alelllll willllsse of s wed w brstougof bage orore **he** anthesww **were** ofawe ce qur **the he** sbaing tthe bytondece nd t llllifsffo acke o t **in** ir **me** hedlff scewant pi t bri pi owasem **the** awh thorathas th **we** hed ofainginictoplid **we me******
正如我们所看到的,生成的文本可能没有任何意义,但是有一些单词和短语似乎形成了一个想法,例如:
****we, band, pack, the, man, where, he, hang, out, be, thing, me, were****
恭喜你,我们已经到了博客的结尾!
结论
在这篇博客中,我们展示了如何使用 PyTorch 的 LSTMCell 和基于递归神经网络 LSTM 和双 LSTM 实现一个架构来制作一个端到端的文本生成模型。
重要的是要说明,文本生成的建议模型可以通过不同的方式进行改进。一些建议的想法将是增加待训练的文本语料库的大小,增加历元的数量以及每个 LSTM 的存储器大小。另一方面,我们可以想到一个基于卷积 LSTM 的有趣架构(可能是另一个博客的主题)。****
参考
[2] 调查:深度学习中的文本生成模型
“让美国重返工作岗位的计划”的文本挖掘评论
戴夫·米库达在 Unsplash 上拍摄的照片
经济,公共卫生,冠状病毒,NLP
上周, 、托马斯·L·弗里德曼在《纽约时报》上发表的一篇文章引起了我的兴趣:“让美国重返工作岗位的计划”。它提倡对新冠肺炎疫情采取数据驱动的方法:也就是说,限制冠状病毒的感染和死亡人数,同时根据最佳数据和专家建议,最大限度地加快我们安全地让工人重返工作场所的速度。
在另一篇专栏文章中,他提供了如何逐步完成这一任务的详细计划。
我很想知道他的读者是如何看待这件事的,所以我决定对这个专栏的评论做一个文本分析:一个让美国恢复工作的计划。在我准备这篇文章的时候,我已经得到了 1000 多条评论。数据可以在这里找到,随意摆弄。
数据预处理
comment_preprocessing.py
图 1
- 在 state 列中,有些是完整的州名,有些是缩写。我们将把它们转换成缩写。
- 一些用户没有标明他们来自哪个州,只标明美国。来自加拿大任何一个省的用户都会被归类到加拿大。
- 下面这段代码是从 Python 字典里借来的,用来把美国各州翻译成两个字母的代码。
state_abbrev.py
电子设计自动化(Electronic Design Automation)
state_group.py
图 2
加州有最多的读者在评论区发表意见,紧随其后的是纽约。马萨诸塞州、宾夕法尼亚州和新泽西州稍微落后一些。
comment_len.py
图 3
- 在上面的分布图中,我们可以看到评论长度是一个右螺旋分布,在平均值的两边有很多峰值。
- 概率密度峰值在 50 个词左右。然而,一些评论可以短到不到 10 个字,而另一些评论可以长到略少于 300 个字。
注释 _ 状态 _ 长度. py
图 4
前五个州的评论长度分布非常相似。
comment_word.py
图 5
不出意外,评论中最常用的词是“人、病毒、经济”等等。
comment_bigram.py
图 5
- 从评论中的热门人物,我们可以看到,人们普遍更关心的是健康而不是经济。
- 他们中的一些人认为这种方法是群体免疫。
- 他们中的一些人羡慕韩国的成功。
- 他们中的许多人表达了他们对疾病致死率的不确定性。
我们将使用 TextBlob 来计算情绪极性,该极性位于[-1,1]的范围内,其中 1 表示积极情绪,-1 表示消极情绪。
comment_polarity.py
图 6
- 从上面的图中,我们可以看到情绪极性分布看起来接近正态分布。
- 还有一个接近 0 的显著峰值,和另一个接近 0.1 的峰值。这表明评论一般倾向于使用简单的语言。
- 平均情绪极性为 0.077,处于低位。
polarity_state.py
图 7
看起来新泽西州读者的情绪极性分布有点偏左。
avg_polarity.py
图 8
果然,新泽西州读者的评论平均情绪极性得分最低。在某种程度上,我们在新泽西没有纽约或加州那么多的读者评论。
Word2vec
我们将使用 Gensim 来训练 word2vec 嵌入评论。关于每行代码如何工作的详细解释可以在另一篇文章中找到。
comment_w2v.py
现在,我们可以试着找出与“人”最相似的词:
w2v_model.wv.most_similar(positive=['people'])
图 9
找出与“病毒”最相似的单词:
w2v_model.wv.most_similar(positive=['virus'])
图 10
找出与“经济”最相似的单词:
w2v_model.wv.most_similar(positive=['economy'])
图 11
有趣的是,“健康”和“经济”之间有多么相似:
w2v_model.wv.similarity('health', 'economy')
主成分分析
PCA 是一种降维方法,它采用几个维度的向量并将其压缩成一个更小的向量,同时保留原始向量中的大部分信息。
我们将使用 PCA 来可视化以下 12 个关键词向量。
comment_pca.py
图 12
- “经济”、“工作”、“风险”、“传染”等词汇在情节底部形成一簇,表示它们经常一起使用。这是有道理的,因为经济与人们承担被感染的风险密切相关。
- “健康”和“护理”这两个词都在图的右端,因为它们经常一起使用。
- “接近”、“测试”、“病毒”和“需要”这几个词在剧情中间形成一簇,表示它们经常一起使用。
结束了
- 综上所述,许多发表意见的读者认为,为了实施这样一个恢复工作的计划,需要进行积极的大规模测试作为基础。
- 许多发表意见的读者认为该计划可能会将人们置于危险之中。
- 鉴于目前的情况,大多数发表意见的读者听起来并不乐观或积极。
Jupyter 笔记本可以在 GitHub 上找到。保持安全和健康。
虚拟文本挖掘:用 Python 进行情感分析
20 行代码中任何 NLP 项目的通用步骤
这篇短文展示了任何文本挖掘项目的一般步骤。如果你想用笔记本来记录,你可以从 这里的 拿到笔记本。
这个目标不是给出文本挖掘的详尽概述,而是快速启动您的思维,并给出进一步增强的想法。
第一步:数据
出于教学目的,我们从一个非常非常小的 6 篇评论的数据集开始。数据通常来自网络搜集评论网站,因为它们是同时具有原始文本和数字评估的良好数据源。
第 2 步:数据准备
数据通常需要比本例中更多的清理,例如 regex 或 python 字符串操作。
文本挖掘的真正挑战是将文本转换成数字数据。这通常分两步完成:
- 词干化/词目化:将所有单词还原到它们的“基本形式”,以使单词计数更容易
- 矢量化:应用基于字数的算法(更高级)
- 在这个例子中,我使用了一个 LancasterStemmer 和一个 CountVecotrizer,这是众所周知且易于使用的方法。
步骤 2a: LancasterStemmer 将单词恢复到它们的基本形式
步骤 2b:count vectorizer 应用单词包(基本上是单词计数)进行矢量化(这意味着将文本数据转换为数字数据)
第三步:机器学习
既然文本已经被转换成数字数据,就使用任何你可以对常规数据使用的方法吧!
我希望这个简短的例子对你的旅程有所帮助。不要犹豫,在评论中提出任何问题。感谢阅读!
完整笔记本链接:在这里。
文本挖掘:冠状病毒隔离期间,人们在家做什么?
对人们在冠状病毒封锁期间的所作所为和感受的探索性和感性分析
莎伦·麦卡琴在 Unsplash 上的照片
随着越来越多的国家宣布全国范围的关闭,大多数人被要求呆在家里并被隔离。我想知道在这个“关闭”期间,人们是如何度过时间的,他们的感受是什么,所以我分析了这篇文章中的一些推文,希望这些数据能给我一些启示。
导入和预处理数据集
对于数据集,我使用 twitteR 库从 Twitter 中提取了 20,000 条带有“#quarantine”和“#stayhome”标签的推文。
在将数据导入 R 之后,我们需要对 tweets 进行预处理并将其标记为单词(标记)以供分析
tweet_words <- tweets %>%
select(id,
screenName,
text,
created) %>%
mutate(created_date = as.POSIXct(created, format="%m/%d/%Y %H")) %>%
mutate(text = replace_non_ascii(text, replacement = "", remove.nonconverted = TRUE)) %>%
mutate(text = str_replace_all(text, regex("@\\w+"),"" )) %>%
mutate(text = str_replace_all(text, regex("[[:punct:]]"),"" )) %>%
mutate(text = str_replace_all(text, regex("http\\w+"),"" )) %>%
unnest_tokens(word, text)
从数据集中移除常用(停用)词
在对数据集进行标记化和预处理后,我们需要删除停用词,如“for”、“the”、“an”等。对分析没用的。
**#Remove stop words**
my_stop_words <- tibble(
word = c(
"https","t.co","rt","amp","rstats","gt",
"cent","aaya","ia","aayaa","aayaaaayaa","aaaya"
),
lexicon = "twitter"
)**#Prepare stop words tibble**
all_stop_words <- stop_words %>%
bind_rows(my_stop_words)**#Remove numbers**
suppressWarnings({
no_numbers <- tweet_words %>%
filter(is.na(as.numeric(word)))
})**#Anti-join the stop words and tweets tibbles**
no_stop_words <- no_numbers %>%
anti_join(all_stop_words, by = "word")
我们还可以使用下面的代码快速检查一下从数据集中删除了多少停用词:
tibble(
total_words = nrow(tweet_words),
after_cleanup = nrow(no_stop_words)
)
结果会是这样的:
右边的数字(155,940)是去掉停用词后剩下的令牌数。
此时,我们的数据集应该是干净的,可以进行分析了!
词频分析
进行文本挖掘的一种常见方法是查看词频。首先,让我们看看我们的推文中最常见的一些词。
单字单词云
排名前五的单词是:
- 隔离—出现 13358 次
- covid 19–出现了 1628 次
- 冠状病毒——出现了 1566 次
- 日—出现了 1200 次
- 家——出现了 1122 次
显然,隔离与冠状病毒/新冠肺炎疫情有关,人们都呆在家里以避免接触病毒。
**Unigram word cloud**
no_stop_words %>%
count(word) %>%
with(wordcloud(word, n, max.words = 100, random.order = FALSE,scale=c(4,0.7),
colors=brewer.pal(8, "Dark2"),random.color = TRUE))
最常见的积极和消极的话
获得词频后,我们可以使用“NRC”词典给每个单词分配一个标签(正面或负面)。然后,我们可以创建一个词云标签。
最常见的正面词和负面词的词云
从云这个词我们知道,在这个隔离期,大部分人都是压力大,无聊的。但从好的方面来看,我们也了解到人们正在发出善意的信息,告诉他人保持安全和健康。
**#Positive and negative terms word cloud**
no_stop_words %>%
inner_join(get_sentiments("bing"), by = c("word" = "word")) %>%
count(word, sentiment, sort = TRUE) %>%
acast(word ~ sentiment, value.var = "n", fill = 0) %>%
comparison.cloud(colors = brewer.pal(2, "Dark2"),
max.words = 100)
情感分析
情感分析帮助我们从文本数据中识别表达的文本和观点。它帮助我们理解人们对某一特定话题的态度和感受。
提取的推文的情感排名
虽然人们担心冠状病毒,但我们大多数人都保持乐观。令人惊讶的是,在隔离期间,人们发的积极词汇比消极词汇多。
**#Sentiment ranking**
nrc_words <- no_stop_words %>%
inner_join(get_sentiments("nrc"), by = "word")sentiments_rank <- nrc_words %>%
group_by(sentiment) %>%
tally %>%
arrange(desc(n))**#ggplot** sentiments_rank %>%
#count(sentiment, sort = TRUE) %>%
#filter(n > 700) %>%
mutate(sentiment = reorder(sentiment, n)) %>%
ggplot(aes(sentiment, n)) +
geom_col(fill = "mediumturquoise") +
xlab(NULL) +
coord_flip() +
ggtitle("Sentiment Ranking") +
geom_text(aes(x = sentiment, label = n), vjust = 0, hjust = -0.3, size = 3)
情感内省——弄清人们的情感
通过使用“NRC”词典,我们还可以将这些词分为八种情绪类型以及积极和消极的术语。
分配标签后,我们可以将情感分组,并生成如下所示的词频图。还要注意的是,一些术语如音乐和金钱可以在多个情感标签下找到。
基于上述情感标签的几点见解:
- 在这段呆在家里的时间里,人们把自己推向金钱,(不)生日,音乐和艺术
- 人们在谈论什么政府:国会和协议
**#Ten types of emotion chart**
tweets_sentiment <- no_stop_words %>%
inner_join(get_sentiments("nrc"), by = c("word" = "word"))tweets_sentiment %>%
count(word, sentiment, sort = TRUE)**#ggplot**
tweets_sentiment %>%
**# Count by word and sentiment**
count(word, sentiment) %>%
# Group by sentiment
group_by(sentiment) %>%
**# Take the top 10 words for each sentiment**
top_n(10) %>%
ungroup() %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(word, n, fill = sentiment)) +
geom_col(show.legend = FALSE) +
facet_wrap(~ sentiment, scales = "free") +
coord_flip() +
ggtitle("Word frequency based on emotion")
可视化单词关系
在进行文本挖掘时,单词关系的可视化很重要。通过将单词排列成“网络”图,我们可以看到单词在数据集中是如何相互连接的。
首先,我们需要将数据集标记为二元模型(两个单词)。然后,我们可以将单词排列成连接节点的组合,以便可视化。
“隔离”数据集的网络图
**#Tokenize the dataset into bigrams**
tweets_bigrams <- tweets %>%
select(id,
# screenName,
text,
created) %>%
mutate(created_date = as.POSIXct(created, format="%m/%d/%Y %H")) %>%
mutate(text = replace_non_ascii(text, replacement = "", remove.nonconverted = TRUE)) %>%
mutate(text = str_replace_all(text, regex("@\\w+"),"" )) %>%
mutate(text = str_replace_all(text, regex("[[:punct:]]"),"" )) %>%
mutate(text = str_replace_all(text, regex("http\\w+"),"" )) %>%
unnest_tokens(bigram, text, token = "ngrams", n = 2)**#Separate the bigrams into unigrams**
bigrams_separated <- tweets_bigrams %>%
separate(bigram, c("word1", "word2"), sep = " ")**#Remove stop words**
bigrams_filtered <- bigrams_separated %>%
filter(!word1 %in% all_stop_words$word) %>%
filter(!word2 %in% all_stop_words$word)**#Combine bigrams together**
bigrams_united <- bigrams_filtered %>%
unite(bigram, word1, word2, sep = " ")
来自网络图的一些有趣见解:
- 隔离期间,人们在推特上写日记
- 在隔离期间,人们听李·摩根的爵士乐
- 在隔离期间,Jojo 的现场表演越来越受欢迎
- 自我隔离是对抗新冠肺炎的一种方式,人们对消除压力的健康窍门和技巧感兴趣
词语相关分析——那么人们对社交距离有什么感觉呢?
隔离或社会距离可能在情感上具有挑战性,我想更多地了解人们在这一时期的感受。
单词相关性使我们能够了解一对单词在一个数据集中出现的频率。它让我们对一个特定的单词及其与其他单词的联系有了更多的了解。
从单词 cloud 中,我们知道“压力”和“无聊”频繁出现在我们的数据集中。于是,我提取了三个词出来:“无聊”、“压力”、“卡住”来看看它的词关联。
隔离期间、待在家中期间的感觉的词相关性
从“无聊”、“压力”和“卡住”的词相关性得到的启示:
- 当人们感到无聊时,他们用抖音和游戏来消磨时间
- 无聊几乎概括了 2020 年大多数人的生活
- 封锁造成了压力,人们在网上寻找减轻压力的方法
- 当人们“被困”在家里时,他们会在网飞看恐怖电影/系列
单词相关性分析——那么,人们在家打发时间都在做什么呢?
为了了解在这段待在家里隔离的时间里,人们在家里做些什么来打发时间,我提取了三个词,“玩”、“读”和“看”,以获得更多的见解。
隔离期间和在家期间操作的单词相关性
从“玩”、“读”和“看”的词相关性得到的启示:
- 大多数人可能在家里玩游戏、看电影和视频来打发时间
- 人们花时间给他们的孩子读书
- 在此期间,人们也终于有时间阅读了
单词相关性分析—生日、金钱和社区…
三个词频繁出现在情绪标签表中,它们是“生日”、“社区”和“金钱”所以,我研究了这个词和其他词的关系。
从“生日”、“社区”和“金钱”的词相关性得到的启示:
- 生日派对取消了。相反,人们在推特上表达他们的愿望
- 人们同意金钱不能阻止我们感染病毒的观点
结论
在冠状病毒关闭期间,我们能够深入了解人们的感受以及他们在做什么,同时仍然遵循社交距离规则。
我们获得的一些主要见解包括:
- 人们对冠状病毒的情况感到紧张,但仍然保持乐观
- Tiktok 和网飞在居家隔离期间被广泛使用
- 人们花更多的时间和孩子在一起,在艺术、音乐和电影上
大家呆在家里,注意安全!
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
基于 R 的文本挖掘:收集和清理数据
凯文·Ku 在 Unsplash 上的照片
印度尼西亚最大媒体评论的推文案例研究。
T ext Mining 是挖掘基于文本格式的数据的过程。这个过程可以获取大量信息,例如人们正在谈论的话题,分析他们对某种话题的情绪,或者了解在给定时间哪些词使用频率最高。
Twitter 是印度尼西亚最受欢迎的社交媒体之一。我也是它的使用者之一。根据 Statcounter 的数据,印度尼西亚有 7.4%的人口在使用互联网。
Twitter 为人们提供了一个平台,在这里他们可以发表自己的意见,也可以根据自己的需要获取信息。这些推文包含大量有待发现的信息。因此,对于那些想要参与文本挖掘的人来说,Twitter 是一个很好的游乐场。
在这篇文章中,我将向你展示如何在 Twitter 上进行文本挖掘,特别是来自印度尼西亚最大的媒体之一 Kompas 的印度尼西亚网民的评论。本文只解释了如何使用 r 收集和清理数据。
在下一篇文章中,我将通过探索、情感分析和主题建模向您展示这些文本数据是如何包含大量信息的。
攻击计划
在我向您展示如何进行文本挖掘之前,让我向您概述一下我们将会遇到的步骤:
- 使用 Twitter API 收集数据
- 数据清理
- 探索
- 主题建模
- 情感分析
在这篇文章中,我将只向你展示第一步和第二步。其余的将在下一篇文章中讨论。
收集数据
我们要做的第一步是从 Twitter 上收集数据。在你收集推文之前,你必须考虑一些方面,比如你想要达到的目标是什么,你想把推文带到哪里,是通过使用一些查询进行搜索还是从一些用户那里收集。
注意:确保您有访问 API 的 Twitter API 密钥。你可以在这里注册并阅读更多信息
在这种情况下,我们只想知道印度尼西亚网民给出的评论是什么,他们在谈论什么,以及推文中的情绪如何。
我们必须从新闻媒体账户获取评论的原因是,他们的评论涉及广泛的话题,而且他们的垃圾推文较少。
我所说的垃圾推文,例如使用某种标签但不谈论它的推文,或者换句话说,断章取义的推文。
基于这个目标,我们将从账户的提及中收集推文,该账户的用户名是 kompascom。如果我们只想获取对它的回复,我们可以使用一个特殊的关键字,即 to:
**# Import the library**
library(rtweet)**# Note: Use your own token**
twitter_token <- create_token(
app = appname,
consumer_key = key,
consumer_secret = secret,
access_token = "xxx",
access_secret = "xxx"
)**# Search the tweets**
news_tweets <- search_tweets("to:kompascom", n = 18000, include_rts = F)**# Select specific columns only**
data_fix <- news_tweets %>%
# Remove Duplicate
distinct(text, .keep_all = T) %>%
# Take The Text Only
select(created_at, text)**# Create id column as the tweet identifier** data_fix["id"] <- 1:nrow(data_fix)**# Convert the created_at to date format**
data_fix$created_at <- as.Date(data_fix$created_at, format = "%Y-%m-%d")
在这种情况下,我们将获取大约 18000 条回复用户名的推文。运行代码后,我们得到了大约 16746 条推文和 90 个专栏。我们不会使用所有的专栏,相反,我们只从推文中选择日期和文本,并且我们还会删除彼此重复的推文。此外,我们必须创建一个 id 列作为 tweet 的标识符。因此,我们将有 16418 条推文和 3 个专栏。
数据清理
数据清理是一个重要的步骤,比文本挖掘中的任何步骤都要花费更多的时间。它之所以重要,是因为我们要删除对其意义没有任何影响的单词和符号。这里有一些还不干净的推文的例子,
从上面我们可以看到,一些推文中包含了我们移除的单词和符号,比如提及(即@ kompascom)、hashtags(即#COVID19)、转义序列(即\n)、UTF 符号(即)等等。
除此之外,我们必须删除对 tweet 的语义没有任何影响的词,我们称之为停用词。印度尼西亚语中的停用词的例子有 tidak、ya、bukan、karena、untuk 等等。对于停用词,我们将使用这个 GitHub 资源库,您可以在这里 下载。
当我们清理推文时,还有一个额外的挑战需要我们去做。大多数印尼人写推文时会缩短,有很多单词,但意思是一样的。
例如,在印度尼西亚语中,如果我们想说“不”,我们会说“tidak”。这个单词有很多书写形式,有“tak”,“ga”,“gak”,甚至他们会使用额外的字母来强调这个单词,如“tidakkkk”。这会让那些想用印尼语分析推文的人头疼。
正因为如此,我们没有任何快速的方法来清理它,所以我们必须给一些时间来做它。
边注,为了使我们的工作更容易,我们不会删除有附加字母的单词,相反,我们只删除停用词和符号。这是因为这个词不经常出现,所以我们分析这个的时候就忽略了。
这是我们将要使用的代码,
**# Get the text column**
text <- data_fix$text**# Set the text to lowercase**
text <- tolower(text)**# Remove mentions, urls, emojis, numbers, punctuations, etc.**
text <- gsub("@\\w+", "", text)
text <- gsub("https?://.+", "", text)
text <- gsub("\\d+\\w*\\d*", "", text)
text <- gsub("#\\w+", "", text)
text <- gsub("[^\x01-\x7F]", "", text)
text <- gsub("[[:punct:]]", " ", text)**# Remove spaces and newlines**
text <- gsub("\n", " ", text)
text <- gsub("^\\s+", "", text)
text <- gsub("\\s+$", "", text)
text <- gsub("[ |\t]+", " ", text)**# Put the data to a new column**
data_fix["fix_text"] <- text
head(data_fix$fix_text, 10)
你可能会对 gsub 函数的工作原理感到困惑。让我给你解释一下。gsub 函数有 3 个参数,它们是使用正则表达式的单词和符号的模式,对它的替换,然后是我们想要处理的字符串或向量。
正则表达式基本上是一种寻找某种格式的单词的模式。在这个例子中,如果您想要删除提及,那么这个模式将看起来像' @\w+',其中@是这个模式的开始。然后\w 标记每个单词字符,范围从 a-z、A-Z 到数字,如 0-9。最后,+表示向它显示一个或多个字符。如果它与模式匹配,那么该单词将被替换为给定的单词或空字符串。
此外,我们必须清理文本,我们必须把它变成一个整洁的数据格式,我们还必须删除停用词。代码看起来像这样,
library(tidytext)**# Remove Stopwords**
stop_id <- scan(paste(getwd(), "/stopwords-id.txt", sep=""), character(), sep="\n")**# Create dataframe of the stop words**
stop_words <- data.frame(
word <- stop_id,
stringsAsFactors = F
)
colnames(stop_words) <- "word"**# Convert to tidy format**
tidy_text <- data_fix %>%
select(created_at, id, fix_text) %>%
**# Tokenize the word from the tweets**
unnest_tokens(input = fix_text, output = word) %>%
**# Remove stop words**
anti_join(stop_words, by="word")
在我们清理和整理推文之后,这是它看起来的样子,
有了这些,我们就可以继续探索它了。
结论
总之,Twitter 是分析文本数据的一个很好的数据集。我们可以从它那里获得很多信息,比如分析它的情绪,知道谈论的话题,等等。印度尼西亚是推特的最大用户之一。因此,有大量的信息需要挖掘和分析。
除了它的潜力,还有一些障碍来分析印尼推文,特别是俚语。正因为如此,我们必须解决分析印度尼西亚推文的公开挑战,以使信息更有价值。
参考
[1] 印尼社交媒体统计。【https://gs.statcounter.com/social-media-stats/all/indonesia
【2】西尔格,j .和罗宾逊,D. 用 R: A Tidy 方法进行文本挖掘 (2017)。奥赖利媒体公司。
文本规范化
图片由来自 Unsplash 的 Markus Winkler 提供,由作者编辑。
为什么,什么,怎么做
在的前几篇文章中,我们花了一些时间解释和实现 NLP 中一些最重要的预处理技术。然而,我们在真实的文本情境中玩得太少了。现在是时候做点工作了。
我们在关于词干的文章中讨论了文本规范化。然而,词干并不是文本规范化中最重要的(甚至是最常用的)任务。我们前面还介绍了其他一些规范化技术,如 标记化句子化 和 词汇化 。但是还有其他一些小任务用来完成这个重要的预处理步骤,这些将在本文中讨论。
请记住,没有适用于所有情况的“正确的”标准化任务集或列表。事实上,随着我们对 NLP 研究的深入,我们越来越了解 NLP 并不像人们想象的那样普遍。虽然有许多有趣的通用工具箱和预制管道,但更精确的系统是那些根据环境量身定制的系统。
因此,请不要把本文中列出的规范化步骤当作硬性规则,而是作为进行文本规范化的指南。同样重要的是要指出,在一些罕见的情况下,您可能不希望对输入进行规范化—在这些情况下,更多的变化和错误是重要的或至关重要的(例如,考虑测试校正算法)。
了解我们的目标—为什么我们需要标准化
让我们从清楚地定义我们想要使用规范化技术的地方开始。自然语言作为一种人类资源,往往遵循其创造者随机性的内在本质。这意味着,当我们“生产”自然语言时,我们将随机状态印入其中。计算机不太擅长处理随机性(尽管这一点正随着机器学习算法的使用而最小化)。
当我们规范化一个自然语言资源时,我们试图减少其中的随机性,使它更接近预定义的“标准”。这有助于减少计算机必须处理的不同信息量,从而提高效率。
通过标准化,我们希望使“文本分布”更接近“正态”分布。图片取自维基百科。
当我们规范化一个自然语言资源时,我们试图减少其中的随机性
在那篇关于词干的文章中,我提到规范化试图使事物更接近“正态分布”。从某种意义上说,当我们对自然语言输入进行规范化时,我们希望让事情“如预期的那样”,以“良好”和“可预测”的形式进行,就像遵循正态分布的概率分布一样。
撇开数学不谈,我们可以讨论一下将输入归一化到我们的 NLP 系统中的好处。
首先,通过减少变化,我们有更少的输入变量需要处理,提高了整体性能并避免了错误否定(想象一下软件日志行如果没有输入错误就会触发警告的情况)。对于专家系统和信息检索任务来说,这是非常正确的(想象一下,如果谷歌的搜索引擎只匹配你输入的精确单词!).
在某种意义上,标准化可以比作“去除锐边”。图片来自国会大厦的建筑师。
第二,特别是在谈论机器学习算法时,如果我们使用简单的旧结构,如单词包或 TF-IDF 字典,规范化会减少输入的维数;或者降低创建嵌入所需的处理量。
第三,规范化有助于在密码破译输入被传递到我们的决策 NLP 算法之前对其进行处理。在这种情况下,我们确保我们的输入在被处理之前遵循“契约”。
最后,如果做得正确,归一化对于从我们的自然语言输入中可靠地提取统计数据是非常重要的——就像在其他领域(如时间序列分析)一样,归一化对于 NLP 数据科学家/分析师/工程师来说是重要的一步。
我们想让什么正常化?
这是一个重要的问题。在做文本规格化的时候,我们要确切的知道我们要规格化什么,为什么要规格化。此外,输入的目的有助于形成我们将要应用的标准化输入的步骤。我们对标准化最感兴趣的有两件事:
- ****句子结构:是否应该总是以一个标点符号结尾?可以有重复的标点符号吗?我们应该去掉所有的标点符号吗?此外,可以使用更具体的结构(如达到主语动词宾语),但更难实现。
- T2:这是需要注意的核心问题之一。大多数时候,我们希望自己的词汇量越小越好。原因是,在 NLP 中,单词是我们的关键特征,当我们在这些方面的变化较少时,我们可以更好地实现我们的目标。
在实践中,我们可以通过分解成更简单的问题来对这两个方面进行规范化。以下是最常见的一些:
→删除重复的空格和标点符号。
→去除重音符号(如果您的数据包含“外国”语言的发音符号,这有助于减少与编码类型相关的错误)。
→去除大写字母(通常,使用小写单词会产生更好的结果。然而,在某些情况下,大写字母对于提取信息非常重要,比如姓名和地点)。
→移除或替换特殊字符/表情符号(例如:移除标签)。
→缩写的替代(英语中很常见;例如:“我是”→“我是”)。
→将单词数字转换成数字(例如:“23”→“23”)。
→替换其类型的值(例如:“50 美元”→“钱”)。
→缩略词规范化(如:“US”→“United States”/“U . s . a”)和缩写规范化(如:“btw”→“by the way”)。
→标准化日期格式、社会安全号码或其他具有标准格式的数据。
→拼写纠正(可以说一个单词可以有无限种拼写错误的方式,因此拼写纠正通过“纠正”来减少词汇的变化)——如果您正在处理开放的用户输入,如推文、即时消息和电子邮件,这非常重要。
→通过词干化或词条化去除性别/时间/年级变化。
→用生僻字代替较常见的同义词。
→停止单词去除(与其说是一种归一化技术,不如说是一种降维技术,但为了方便提及,我们把它留在这里)。
在本文中,我将只讨论其中一些的实现。
怎么做正规化
为了选择我们将要使用的标准化步骤,我们需要一个特定的任务。对于本文,我们将假设我们想要提取 2020 年 3 月底期间提取的一组 3000 条推文对# cov 白痴标签的情绪,以了解世界各地的人们对新冠肺炎疫情的行为。
我提前得到了这些推文,可以在这里下载。我还继续使用这个名为better-promaly的漂亮工具检查文本中的诅咒,如果你愿意,可以将它添加到你的规范化管道中。它们也不包含写内容的人。
然而,我没有继续努力删除每条推文中的名字或检查任何政治立场、假货等,因为这不是本文的目的,可能需要另一整篇文章(关于自动审查)。
我想澄清的是,我对推文的内容不承担任何责任,因为它们是由作者在决定在 twitter 上发布他们的言论的那一刻有意识地公开的。我刚刚批量下载了微博。考虑到这一点,让我们继续(下面是一篇介绍如何使用 python 挖掘推文的好文章的链接)。
** [## 使用 Tweepy 在 Python 中获取和使用 Twitter 数据
完成本教程后,您将能够:连接到 twitter RESTful API,使用…
www.earthdatascience.org](https://www.earthdatascience.org/courses/earth-analytics-python/using-apis-natural-language-processing-twitter/get-and-use-twitter-data-in-python/)
特别是对于这种情况,我们想要应用以下步骤:去除重复的空白和标点符号;收缩的替代;拼写更正。同样,我们已经讨论过 词汇化 。所以,我们正在使用它。
在我们完成代码部分之后,我们将对应用上述规范化步骤的结果进行统计分析。
关于规范化的一个重要的事情是函数的顺序很重要。我们可以说规范化是 NLP 预处理管道中的一个管道。如果不小心,我们可能会删除对后续步骤很重要的信息(比如在 lemmatizing 之前删除停用词)。
就像在生产线上一样,标准化步骤的顺序很重要。图片由 Pinterest 的 Pattama Pon 提供。
我们甚至可以将这些步骤分成两个连续的组:“预标记化 步骤】(用于修改句子结构的步骤)和“后标记化步骤”(用于仅修改单个标记的步骤 ) ,以避免重复标记化步骤。然而,为了简单起见,我们使用一个简单的。split()函数。
在我们将 tweets 解析成字符串列表后,我们可以开始创建函数了。顺便说一句,我在列表周围使用了这个叫做 tqdm 的漂亮模块,所以一旦我们应用了标准化过程,我们就有了漂亮的进度条。以下是需要的导入:
from symspellpy.symspellpy import SymSpell, Verbosity
import pkg_resources
import re, string, json
import spacy
from tqdm import tqdm
#Or, for jupyter notebooks:
#from tqdm.notebook import tqdm
删除重复的空格和重复的标点符号(和 URL):
- 完成简单的正则表达式替换。还有改进的空间,但是做到了我们所期望的(这样,我们就不会有多种尺寸的沉默和感叹号标记)。我们删除了 URL,因为这大大减少了我们拥有的不同标记的数量(我们首先这样做,因为标点替换可能会杀死它)。
缩写的替代:
- 使用来自维基百科的缩写列表,我们遍历句子,并用缩写替换它们的实际单词(这得益于在标记化之前发生,因为一个标记被分成两个)。这有助于以后更好地构建句子。名单可以从这里下载。
拼写更正:
- 这是一个棘手的问题。这可能(并且将会)导致一些不必要的变化(大多数拼写纠正词典缺少重要的上下文单词,因此它们被认为是拼写错误)。所以你要有意识地使用它。做这件事有许多方法。我选择使用一个名为 symspellpy 的模块,它真的很快(这很重要!)而且工作做得相当不错。另一种方法是训练一个深度学习模型,根据上下文进行拼写纠正,但这完全是另一回事。
词汇化:
- 如果您一直关注我的系列文章,那么您应该知道我已经实现了自己的 lemmatizer。然而,为了简单起见,我在这里选择使用 good old spaCy。它既快速又简单,但是您可以使用任何您想要的其他工具。我还决定删除(替换)任何标签,并在这里提及。对于情感分析,我们并不真的需要它们。
最后,我们将所有步骤连接在一个“管道”功能中:
在 Google Colab 笔记本上运行的规范化管道。图片作者。
结果
所以,你可能想知道:应用这些任务的结果是什么?我运行了一些计数函数并绘制了一些图表来帮助解释,但我必须明确一件事:数字不是表达文本规范化重要性的最佳方式。
相反,当应用于下游 NLP 应用程序时,文本规范化通过提高效率、准确性和其他相关分数来发挥其最佳作用。我会指出一些我们可以通过统计数据清楚看到的好处。
首先,我们可以清楚地看到不同令牌的总数减少了。在这个具体的例子中,我们减少了大约 32%的令牌数量。
在对我们的数据进行标准化之后,我们将令牌的数量减少了大约 32%。图片作者。
Distinct words in unnormalized: 15233–80% of the text correspond to 4053 distinct words.
Distinct words in normalized: 10437–80% of the text correspond to 1251 distinct words.
现在,一个更大的不同发生在普通令牌的数量上。这些记号是对应于所有记号的大约 80%的记号。通常,我们有大约 10-20%的标记,占文本的 80%。
通过应用标准化,我们将最常见的令牌数量减少了 69% !太多了!这也意味着,我们插入这些数据的任何机器学习技术都将能够更好地推广。
归一化后,最常见的标记数减少了 69%。图片作者。
现在,关于文本规范化的一件重要的事情是,为了使它有用,规范化的文本必须保留默认的自然语言结构。我们可以从数据分布本身看出这一点。一个例子是,如果做得好,规范化后句子不会小很多,也不会大很多。
这呈现在下面的直方图中,直方图表明,虽然归一化后 1 大小的句子较少,2 大小的句子较多,但分布的其余部分遵循未归一化数据的结构(另外,请注意,我们的曲线倾向于稍微接近正态分布曲线)。
标准化对整个句子结构几乎没有影响。图片作者。
另一个帮助我们形象化的工具是箱线图。它显示了我们的数据是如何分布的,包括平均值、四分位数和异常值。总之,我们希望我们的中线与非标准化数据的中线相同(或接近)。我们还希望我们的盒子(大部分数据的分布)保持在类似的位置。如果我们能够增加盒子的大小,这意味着我们在中位数周围的数据比标准化之前更多(这是好的)。此外,我们希望减少离群点(那些在我们胡须范围之外的点)。
在标准化之后,我们能够增加四分位范围(大多数标记所在的位置)。我们也保持了相同的中线,减少了异常值。这意味着我们没有破坏我们的文本,但使它不那么复杂=)。图片作者。
如果您想了解我是如何实现这些结果的,同时能够访问和运行我上面提到的所有代码,请查看下面的 Colab 笔记本:
NLP 系列—文本规范化
colab.research.google.com](https://colab.research.google.com/drive/1U_C_4wAtlWQdaA84yVwHUCdkvQWEd7r9)
结论
在这篇文章中,我希望能够解释什么是文本规范化,为什么我们应该这样做,以及如何去做。此外,我试图提出一些证明,证明它的工作(没有提出它的好处)。
如果您一直在关注这个系列,并且在问自己关于我正在开发的工具集,以及我是否给它添加了规范化,那么答案是肯定的!我只是没有在这里使用它,以使它更容易解释。但简而言之,我添加了一些功能,允许(大部分)提到的规范化步骤直接应用于我们的文档或句子结构(并使用我们之前开发的标记化工具)。
您可以通过查看以下提交的状态来了解我的情况:
在我的中级 NLP 解释系列中创建的一组 NLP 工具。-先生/女士
github.com](https://github.com/Sirsirious/NLPTools/tree/1c2e3efd740f1ede57d316d8f4edf18cce295103)
现在我们有了许多工具,我们必须开始应用它们。但是在此之前, 如何才能将文本转化为特征供机器学习算法使用 ?这是下一篇文章的题目!
这里有几个链接和一份额外研究文件:
[## 文本规范化
它是 NLP 管道的一部分,用于预处理文本数据规范化=将一些语言模型应用于…
mlwiki.org](http://mlwiki.org/index.php/Text_Normalization) [## 文本标准化的编码器-解码器方法
摘要文本规范化的任务是映射非规范语言,典型的语音转录和…
www.aclweb.org](https://www.aclweb.org/anthology/W18-3902/)**
基于数据描述的文本预处理
使用新的 Python EDA 包清理杂乱和无结构的文本
图片来自 Unsplash
有没有像无结构文字这么乱的东西?大概不会。无论是谁发明了大写字母、标点符号和停用词,显然都没有考虑到这些东西对主题建模的影响,而只关心它们对人类的可读性。他们真不体谅人。足够幸运的是,我们不必费力地清理我们自己的非结构化文本,因为我们之前的人已经编写了工具,可以让我们轻松地做到这一点。
Data-description就是这样一个工具,它是一个开源包,是为了让数据科学家用来加速他们的探索性数据分析过程。虽然这个包除了处理文本之外还有更多特性,但本文的剩余部分将探索使用 data-describe 清理混乱的数据,并在之后展示它的一些主题建模功能。充分披露:我是数据描述的作者之一,所以我不是一个不带偏见的来源。然而,这不仅仅是一个无耻的插件,所以继续阅读,看看这个工具到底有多有用。
我们可以从导入 data-describe 中的文本预处理函数以及我们将使用的 scikit-learn 数据集开始。这些类别是随机挑选的,所以请随意试验,看看不同的结果。
from data_describe.text.text_preprocessing import *from sklearn.datasets import fetch_20newsgroupscategories = ['rec.sport.baseball', 'rec.autos', 'sci.med', 'sci.space']
text_docs = fetch_20newsgroups(subset='train', categories=categories)['data']
让我们来看看我们多样化语料库中的第一个文档:
作者图片
恶心。大量的换行符、大写字母和标点符号需要去掉。我们的工作是注定要做的。让我们从“标记化”我们的文本开始,或者将单个字符串文档分割成一个单词列表。
当我们浏览这个文本预处理模块的单个函数时,有两件事情要记住:
- 对于显示的每个预处理函数,输出将是一个字符串列表
- 在代码中,各个预处理函数将被另一个函数“to_list”包装。这是因为这些函数是生成器,这意味着它们遍历一个列表来返回值。这为更高效的处理(如多重处理)提供了可扩展性。
tokens = to_list(tokenize(text_docs))
tokens[0]
作者图片
因此,在这一点上,我们已经把我们的文档变成了一个单词列表,或者称为“单词包”格式。我们的下一步是确保一切都是小写。
lower = to_list(to_lower(tokens))
lower[0]
已经改成小写的单词全部高亮显示。(图片由作者提供)
进步!接下来,我们将删除标点符号。这个功能可以删除或替换标点符号。默认情况下,仅当标点符号是尾随/前导实例时才删除标点符号,但也可以设置为删除所有标点符号实例。
no_punct = to_list(remove_punct(lower))
no_punct[0]
红色块用于标识以前包含标点符号的区域。(图片由作者提供)
删除标点符号后,我们可以继续删除数字和任何包含数字的单词。
no_digits = to_list(remove_digits(no_punct))
no_digits[0]
红色块用于标识以前包含数字或带数字的单词的区域。(图片由作者提供)
下一个函数将删除文本中的空格或单个字符。
no_single_char_and_space = to_list(remove_single_char_and_spaces(no_digits))
no_single_char_and_space[0]
红色块用于标识以前包含空格或单个字符的区域。(图片由作者提供)
接下来是停用词的删除。在主题建模中,其思想是通过文档中对文档意义至关重要的重要单词来分隔文本文档。为此,在这个意义上帮助不大的常用词,如“the”、“what”、“thes”等。从文档中删除。虽然下面的函数从文档中删除了所有 NLTK 定义的停用词,但是该函数也可以将附加的自定义停用词列表作为参数来删除这些停用词。
no_stopwords = to_list(remove_stopwords(no_single_char_and_space))
no_stopwords[0]
红色块用于标识以前包含停用词的区域。(图片由作者提供)
后续功能可用于对文档进行“词汇化”或“词干化”。这个想法是将相似的单词组合在一起,这样在进行主题建模时就可以识别它们。例如,对单词“breaking”和“broke”进行词汇化将返回“break”。同样,复数和单数单词将被视为相同。这两种不同技术的主要区别在于,词汇化的单词仍然是真实的单词,而对于词干化的单词则不是这样。下面的例子。
lem_docs = to_list(lemmatize(no_stopwords))
lem_docs[0]
已被词条化的单词被突出显示。(图片由作者提供)
stem_docs = to_list(stem(no_stopwords))
stem_docs[0]
突出显示已被词干化的单词。(图片由作者提供)
完成所有这些后,我们可以将单词列表返回到单字符串文本文档格式。
clean_text_docs = to_list(bag_of_words_to_docs(no_stopwords))
clean_text_docs[0]
作者图片
因此,从最初的未处理的文本文档开始,我们已经走过了很长的路。此时,每个人可能都想知道是否真的有必要输入所有这些函数来完成这项工作?不要担心!有一个功能,只需快速按下一个按钮,就可以应用所有这些步骤。但是等等!如果有必要在所有预处理过程中应用自定义步骤,该怎么办?再说一次,不用担心!定制函数可以混合到预处理中。下一段代码将演示如何操作。
查看其中的一些文档,似乎需要比一般的预处理步骤更多的步骤来从中提取相关信息。
作者图片
提示正则表达式。
import re
pattern = re.compile(r"[\s\S]*\nLines:([\s\S]*)")def extract_lines(docs):
for doc in docs:
extract = pattern.match(doc).groups(0)[0]
yield extract
让我们看看在上面的正则表达式模式上匹配后文档是什么样子。
作者图片
从文档中删除主题和发件人(剧透:文档是电子邮件)似乎是一个好主意,这样可以确保文档的内容在我们进行主题建模时仍然相关。这就把我们带到了圣杯预处理函数(注意:来自 data-describe 的函数可以在自定义管道中使用字符串指定,而自定义函数作为函数对象输入)。
clean_text_docs = preprocess_texts(text_docs, custom_pipeline=[
extract_lines,
'tokenize',
'to_lower',
'remove_punct',
'remove_digits',
'remove_single_char_and_spaces',
'remove_stopwords',
'stem',
'bag_of_words_to_docs'
])
clean_text_docs[0]
作者图片
好多了。至此,我们已经为主题建模做好了准备。数据描述在这里也有帮助,它有一个“小部件”,允许选择主题建模,无论是 LDA、LSA、LSI、NMF 还是 SVD。虽然这一切都是可定制的,以及主题的数量和许多其他参数,如果没有输入参数,该函数将自动检查两个主题到十个主题的模型的一致性值。具有最高一致性值的主题的数量是将被返回的模型。
from data_describe.text.topic_modeling import topic_modellda_model = topic_model(clean_text_docs, model_type='LDA')
我们可以做的第一件事是查看每个主题最重要的单词,这可以通过在新的单元格中显示模型对象来完成。
lda_model
作者图片
这里我们可以做一些观察。我们必须记住,这些单词是作为预处理管道的一部分进行词干提取的。一般看题目,“题目 1”明显是关于棒球的,“题目 2”明显是关于外太空的。模型是如何决定主题数量为 6 的?让我们来看看不同主题数量的每个模型的一致性值。
lda_model.elbow_plot()
作者图片
正如所料,具有六个主题的模型的一致性值最高。接下来,我们可以查看与每个主题相关的热门文档。自然,文档比 Pandas 可能习惯的要长,所以我们可以首先增加数据帧的列宽。
pd.set_option('display.max_colwidth', 500)
top_docs = lda_model.top_documents_per_topic(clean_text_docs_regex)
top_docs = top_docs[['Topic 1', 'Topic 2']]
top_docs.head(3)
作者图片
我们可以仔细看看下面主题 1 的顶部文档。
top_docs['Topic 1'].iloc[0][:700]
作者图片
正如所料,这份文件是关于棒球的。纵观我们的主题,我们看到,虽然前两个主题的含义很清楚,但其他主题并不清楚。这可能有许多不同的原因。众所周知,主题建模在较长的文档中效果更好,但在较短的文本中效果不佳,比如上面提到的电子邮件。可能需要更多的正则表达式处理来处理电子邮件中的引用线程和回复链。也许需要更多的调整,从查看词频开始(这还不是 data-describe 的一个特性,但是我们很乐意接受贡献的努力!).EDA 只是建模过程中的第一步,这就是 data-describe 要帮助的。
上面提到的所有内容都是对使用 data-describe 的文本预处理和主题建模的很好介绍,但这只是对该包特性的皮毛。其他包括数据汇总、聚类、关联、热图、分布图、功能排名、散点图、维度缩减等,同时还提供对大数据和敏感数据的支持。作者(包括我自己)和开源社区正在不断地添加新的特性和增强功能,因此,如果您有任何问题,请随时联系我们,或者立即开始投稿!
Twitter 数据的文本处理技术
图片来自 Pixabay
使文本数据模型就绪
有大量文本格式的数据。对机器来说,分析文本格式的数据是最复杂的任务,因为机器很难理解文本背后的语义。为此,我们将文本数据处理成机器可理解的格式。
文本处理只是将文本格式的数据转换成数值(或向量),这样我们就可以将这些向量作为机器的输入,并使用代数的概念来分析数据。
但是,当我们执行这种转换时,可能会丢失数据。关键是在转换和保留数据之间保持平衡。
在处理文本之前,文本的预处理是必不可少的。要快速浏览一下什么是文本预处理,请查看这篇文章。
自然语言处理的第一步
medium.com](https://medium.com/@ramyavidiyala/a-handbook-to-text-preprocessing-cc9693bf3c12)
在本文中,我们将讨论执行文本处理的各种技术。
在开始之前,让我们先了解几个常用术语。
- 每个文本数据点称为一个 文档
- 整套文件叫做 文集
可以使用以下技术来完成文本处理,
- 一袋单词
- TF-IDF
- Word2Vec
现在让我们开始详细研究每种技术。
1.一袋单词
单词包通过使用一个独特单词的字典来完成文档到矢量的简单转换。这只需两步即可完成,
第一步:构建字典
以向量形式创建数据语料库中所有唯一单词的字典。设语料库中唯一词的数量为,‘d’。所以每个单词是一个维度,因此这个字典向量是一个 d 维向量。
步骤 2:构建载体
对于每个文档,比如说,rᵢt23】我们创建一个向量,比如说, v ᵢ.
现在,这种具有 d 尺寸的 v ᵢ有两种构造方式:
- 对于每个文档,根据字典构建 v ᵢ,使得字典中的每个单词按照该单词在文档中出现的次数来再现。
- 对于每个文档,根据字典构造 v ᵢ,使得字典中的每个单词被再现,
- 如果该单词出现在文档中,则为 1,或者
- 如果文档中没有该单词,则为 0
这种类型被称为二进制包字。
现在,我们有了每个文档的向量和一个字典,该字典有一组数据语料库的唯一单词。这些向量可以通过以下方式进行分析
- 在 d 维空间中绘图或
- 计算向量之间的距离以获得相似度(向量越差,越相似)
词汇袋的局限性:
- 问题:包词不考虑词的语义。意思是具有相同语义的单词如美味、可口被分成两个不同的单词。
解决方案:使用词干化和词汇化等技术对数据进行预处理。 - 问题:单词包没有保留文档的顺序信息,这意味着“不好”,这显然是不好的。一袋字把它归类为‘不好’和‘好’,显然不好。
解决方案:不是创建每个单元格都是一个单词的向量,我们可以创建每个单元格都有两个单词的向量(称为二字组)或三个单词的向量(称为三字组)。使用这些n-gram,可以保留顺序信息。然而,当我们使用 n-grams 而不是 uni-gram 时,特征的维数增加了。
2.术语频率—逆文档频率
这里有三个元素——单词、文档、语料库。词频——逆文档频率,简称 TF-IDF,利用这些之间的关系将文本数据转化为向量。
词频说的是一个词和一个文档之间的关系。鉴于,逆文档频率表示单词和语料库之间的关系。
步骤 1:计算词频
词频 是单词 w ⱼ在文档 r ᵢ.出现的概率并且计算如下:
用数学公式计算复习中每个单词的词频
如果一个词在某篇评论中的词频是高则暗示该词在该篇评论中是频繁。如果一个单词在评论中的词频低意味着,该单词在该评论中罕见。
步骤 2:计算 IDF
逆文档频率 表示该词在整个语料库中出现的频率。并且计算如下:
计算逆文档频率的数学公式
如果逆文档频率低,则暗示该词在语料库中频繁。如果逆文档频率是高,则暗示该词在语料库中是稀有。
使用对数而不是简单的反比的原因是缩放。概率范围在 0 和 1 之间的术语频率。当我们简单地取这个反比时,它将是一个巨大的值,因此整个 TF-IDF 值将偏向 IDF。这是在 IDF 术语中使用日志的一个简单且被广泛接受的原因。
评论中一个词的 TF-IDF 是 TF(word,review) *IDF(word,document corpus)。
现在在每个文档的向量形式中,我们有这个单词的 TF-IDF。使用 TF-IDF 值将文档转换成矢量称为 TF-IDF 矢量化。
TF-IDF 矢量化对以下单词给予高度重视
- 在文档中频繁出现(来自 TF)
- 语料库中罕见(来自 IDF)
3.Word2Vec
在单词袋和 TF-IDF 中,我们将句子转换成矢量。但是在 Word2Vec 中,我们将 word 转换成一个向量。因此得名,word2vec!
Word2Vec 将大型文本语料库作为其输入,并产生一个向量空间,通常具有数百个维度,语料库中的每个唯一单词都被分配一个空间中的相应向量。单词向量位于向量空间中,使得语料库中共享共同上下文的单词在空间中彼此靠近。
优势:
- 每个单词在空间上更接近具有相同语义的单词(意思像女人、女孩)
- 它保留了单词之间的关系(男人对女人的向量平行于国王对王后的向量)
履行
在本文中,我们将在 Kaggle 的 Twitter 数据集上使用客户支持。
Twitter 上最大品牌的 300 多万条推文和回复
www.kaggle.com](https://www.kaggle.com/thoughtvector/customer-support-on-twitter)
关于数据集:
Twitter 数据集上的客户支持是一个大型的现代推文和回复语料库,有助于自然语言理解和会话模型的创新,并用于研究现代客户支持实践和影响。该数据集提供了 Twitter 上消费者和客户支持代理之间的大量现代英语对话。
让我们使用预处理过的数据,并对其执行每个文本预处理。让我们看看预处理的数据
data["preprocessed_text"]
预处理数据
实现单词包
Sckit learns 为我们提供了许多图书馆。这使得我们很容易在一行代码中实现任何功能。
from sklearn.feature_extraction.text import CountVectorizer
Sckit learn 提供了 CountVectorizer 来执行单词包。
bow=CountVectorizer( min_df=2, max_features=1000)
bow.fit(data['preprocessed_text'])
bow_df=bow.transform(data['preprocessed_text']).toarray()
创建计数矢量器对象,用预处理后的数据进行拟合、变换。 bow_df 是一个稀疏向量,它包含的 0 的数量多于 1。
实施 TF-IDF
from sklearn.feature_extraction.text import TfidfVectorizer
Sckit learn 提供 tfidf 矢量器来执行 TF-IDF 矢量化。
tfidf = TfidfVectorizer( min_df=2, max_features=1000)
tfidf.fit(data['preprocessed_text'])
tfidf_df=bow.transform(data['preprocessed_text']).toarray()
tfidf_df 包含文档中每个单词在 1000 维向量中的 tf-idf 值。
Word2vec 的实现
谷歌提供了一个庞大的向量列表,这些向量是在谷歌新闻的海量数据上训练出来的。要使用这些向量,我们需要导入 Gensim。
import gensim
tokenize=data['preprocessed_text'].apply(lambda x: x.split())
w2vec_model=gensim.models.Word2Vec(tokenize,min_count = 1, size = 100, window = 5, sg = 1)
w2vec_model.train(tokenize,total_examples= len(data['preprocessed_text']),epochs=20)
输出:(18237,22200)个数据点。这意味着文本的 18237 个数据点现在被转换成 22200 维向量。
什么时候用什么?
这个问题没有明显的答案:它确实取决于应用程序。
例如,单词包通常用于文档分类应用,其中每个单词的出现被用作训练分类器的特征。
TF-IDF 被谷歌等搜索引擎用作内容的排名因素。
当应用程序是关于理解单词的上下文,或者检测单词的相似性,或者将给定的文档翻译成另一种语言,这需要关于文档的大量信息时, Word2vec 就派上了用场。
谢谢你的阅读。以后我会写更多初学者友好的帖子。请在媒体上关注我,以便了解他们。我欢迎反馈,可以通过 Twitter ramya_vidiyala 和 LinkedIn RamyaVidiyala 联系我。快乐学习!
自然语言处理中的文本情感分析
问题、用例和方法:从简单到高级
人们喜欢表达情感。开心还是不开心。喜欢或者不喜欢。表扬或者抱怨。无论好坏。也就是正的或者负的。
NLP 中的情感分析就是从文本中解读这样的情感。是正、负,都有,还是都没有?如果有情感,文本中的所指的对象以及实际的情感短语如差、模糊、便宜、……(不仅仅是正或负。)这也被称为基于方面的分析【1】。
作为一种技术,情感分析既有趣又有用。
首先是有趣的部分。判断一个文本的情感是积极的、消极的、两者都有还是都没有并不容易,至少对计算机算法来说是这样的。线索可能很微妙。撇开总体情绪不谈,更难区分文本中的哪些对象是哪种情绪的主题,尤其是当正面和负面情绪都涉及时。
接下来,到有用的部分。这很好解释。卖东西的人想知道人们对这些东西的感受。它叫做客户反馈😊。忽视这一点对企业不利。
还有其他的用途。如意见挖掘,即试图弄清楚谁持有(或曾持有)什么意见。根据约翰·史密斯的说法,冠状病毒会在六个月内消失。该任务可以形式化为寻找(源、目标、意见)三元组。在我们的例子中,来源 = 约翰·史密斯,目标 = 冠状病毒,意见 = 将在六个月内简单消失。
(多)例
情感分析就是你可能会说的长尾问题。许多不同的场景和微妙之处。这类问题通常最好用例子来描述。
首先,让我们看看一些简单的积极因素。
Amazing customer service.
Love it.
Good price.
接下来,一些积极和消极的东西更难区分。
**Positives**:
What is not to like about this product.
Not bad.
Not an issue.
Notbuggy.
**Negatives**:
Not happy.
Not user-friendly.
Notgood.
**Definitely not positive**:
Is it any good?
上面列表中的积极因素并不是最强的。也就是说,它们特别适合训练 ML 算法来进行关键区分,因为我们绝对不希望这些正面预测为负面。
**Positives**:
Low price.
**Negatives**:
Low quality.
这些实例对于训练 ML 算法进行关键区分特别有用。
**Positives**:
**Quick** turn-around.
**Negatives**:
**Quick** to fail.
同样的观点也适用于此。
**Positives**:
Inexpensive.
**Negatives**:
cheap.
同样的观点也适用于此。
最后,一些比较难破译的底片。
I didn’t get what I was promised.
My package hasn’t arrived yet.
No one in your team has been able to solve my problem.
I was put on hold for an hour!
Why is this so hard to use?
Why does it fail so easily?
用例
很容易想象很多。下面是一些主要的具体的。
- 发现对你的产品或服务的负面评价。在博客帖子、电子商务网站或社交媒体上。更广泛地在网络上的任何地方。
- 对金融工具的总体情绪。比如具体的股票。最近市场对 xyz 股票的看法如何?此外,基于方面的变体。例如根据金融公司 xyz 的分析师预测,abc 的股票很可能在未来一年上涨 20%。辨别是谁的意见提供了更多的信息,可用于评估可信度或缺乏可信度。
- 确定人们对你的产品或服务的哪些部分有所抱怨?特别强烈。优先考虑战术或长期改进。
- 跟踪特定产品或服务(或其中一系列)的客户情绪随时间的变化。检查情况是否有所好转…
- 随着时间的推移,追踪政客们不断变化的观点。个人或团体,如政党。新闻媒体喜欢这么做。为了给诸如这样的唠叨问题火上浇油,你当时是这么说的,但现在却是这样!。
计算问题
到目前为止,我们所讨论的可以归结为两个不同的计算问题。
- 文本的整体情绪是什么:正面,负面,都,还是都不?
- 哪种情绪适用于文本的哪一部分。这也被称为基于方面的情感分析。
先说第一个问题,我们称之为情感分类。
情感分类问题
输入是文本。我们寻求的输出是情绪是积极、消极、都是还是都不是。在这个问题的一个变体中,我们将不会在这里讨论,我们感兴趣的是额外预测积极和消极情绪的强度。你可以想象为什么。xyz 手机真烂比消极多了我对 xyz 手机有点失望。
基于字典的方法
最简单的方法是创建两本词典,分别收录带有积极和消极情绪的词汇。就术语而言,我们指的是一个词或一个短语。基于文本中的术语在这两个字典中的命中,文本被分类为肯定的或否定的。如果一个文本不符合任何一个字典,则该文本被分类为中性的。如果一个文本在两个词典中都找到,则该文本被分类为阳性和阴性。
当一个人希望快速获得某种程度上有效的情感分类器,并且没有足够丰富的标注有情感的文本数据集时,这种方法是值得考虑的。简单是一个原因。更重要的原因是,机器学习替代方案有自己的障碍需要克服。当我们讨论这个主题时,我们将详细研究这些。
尽管存在机器学习障碍,但基于字典的方法迟早会遇到质量问题。因此,如果各种情感类别的高精度和高召回率在您的用例中很重要,您应该考虑提前咬紧牙关投资 ML。如果你能找到一个足够丰富的带标签的数据集,或者想出一些创造性的方法来获得一个数据集,你的任务会变得容易得多,可能需要一些额外的轻量级 NLP(在下一节中讨论)。
监督学习挑战
第一个挑战是必须有一个大而多样的文本数据集,这些文本都标有它们的情感类别:正面、负面、两者都有,或者都没有。
问题是。把文本想象成一个向量。目前在通常的向量空间模型中,即作为一个单词包。也就是说,即使在某种程度上,这个挑战也适用于单词嵌入。
向量空间是巨大的。词典中的每个单词都有一个维度。
这个空间中的绝大多数词语都不带感情色彩。训练机器学习分类器需要一个庞大的训练集。它要做的大部分事情是学习哪些单词是“讨厌的”单词。也就是说,抛弃在学习过程中收集到的偏见(见下面的例子)。
让我们看一个例子,从这个例子中,分类器可以学习错误地将中性词与积极或消极的情绪联系起来。
xyz phone sucks → negative
它将学会把单词 phone 与情绪 negative 联系起来。显然我们不想这样。要忘记这一点,需要训练带有单词 phone 的集合实例,这些实例被标记为not(即 neutral )。
也就是说,将一个庞大而多样的语料库(如维基百科)分解成句子,并给每个句子标上中性的标签可能会缓解这个问题。直觉是这样的。所有的词最初都会学会中性。在标记为负面的文本中重复出现的词语,如吮吸,最终将从它们的中性标签中“逃离”。
超越文字袋功能
从我们在前面部分看到的带标签的例子来看,似乎一个“?”是情绪的预测者。这在直觉上是有意义的。怀疑论者提出问题。不是真正的信徒。
利用字典作为特征
如果我们已经有了与积极或消极情绪相关的短语词典(或者发现它们很容易构建),为什么不把它们用作附加特征呢?它们不必是完整的。只是策划。所以我们可以利用他们的品质。
更详细地说,这就是方法。说不好在否定的字典里。我们将为这个条目创建一个布尔特征。如果不良出现在文本中,该特性的值为 1,否则为 0。我们还可以将条目(不好,不好)添加到我们的训练集中。注意,这里我们是把不好的当做全文。
使用词性?
感情丰富的词往往是形容词。这让人怀疑使用文本中每个单词的词性信息是否有用?作为附加特征或用于修剪特征。
让我们先来看看各种例子中单词的词性。这一分析是使用在线贴标机[2]完成的。
情感承载句中各种词的词性
这引发了什么思考?词性标签形容词似乎与情绪极性(积极或消极)显著相关。词尾副词也。限定词、介词、代词似乎预示着中性阶层。
我们如何利用这一点呢?我们可以在他们的词类上设置词袋特征。例如,过滤掉所有词类标签为限定词、介词或代词的单词。这可以被视为停用词移除的一种精心设计的形式。
特征工程:一些观察结果
尽管这些观察是一般性的,但它们特别适用于我们的问题(情感分类)。
首先,在我们添加一个新功能之前,我们不需要强有力的证据。仅仅是一个微弱的信念,认为它可能会有所帮助。机器学习算法将计算出该特征的预测性,可能结合其他特征。随着时间的推移,训练集越来越丰富,如果可能的话,ML 将自动学习更有效地使用这个特性。弱的特征可以叠加。
这样做的唯一缺点是,如果我们做得太过火,即添加太多的功能,功能空间爆炸可能会回来困扰我们。稍后会详细介绍。
让我们扩展一下“认为它可能有帮助的微弱信念”。在这里,“帮助”仅仅意味着该特征是某种情感类别的预测。我们不需要知道是哪一个。军情局会解决这个问题的。即哪个特征值预测哪个情感类。相比之下,当建立一个基于规则的系统(其中字典是一个特例)时,人们必须指定特征值的哪些组合预测哪个情感类别。
这种风险的特点是空间爆炸吗?
我们已经接受了使用单词袋特征会扩大我们的特征空间。由于前面讨论过的原因,我们决定在这方面咬紧牙关。问题是,本节提到的额外特性会让事情变得更糟吗?
实际上他们会做得更好。让我们通过这个来推理。首先是问号功能。它是布尔值。这里没有爆炸。接下来,基于字典的特性。这些事实上减少了单词向量空间中的噪音,因为它们表现出了情感丰富的单词和短语。最后,词性特征。如所建议的那样使用它们进行过滤(即去除单词),修剪特征空间。
Word k 歌功能?
我们故意把它放在前一节之后,因为如果做得不好,它会有扩大特征空间的更大风险。字k-克即使加上 k = 2 的空间也是巨大的。也就是说,明智地修剪这个空间可能会增加这些功能的成本效益比。
以下是一些值得考虑的合理想法。在讨论中,我们将自己限制在 k=2,即二元组,尽管它适用于更普遍的情况。
- 从模型中删除在训练集中没有足够支持的二元模型。(二元模型的支持度是指它在训练集中出现的次数。)
- 对于额外的修剪,也要考虑词性。
盘点…
我们将通过评估我们在这里讨论的内容及其含义来结束这一部分。首先,我们看到 ML 方法可以被赋予各种各样的特性。我们只是简单地将特性混合在一起。只要每个包含都有合理的理由。我们不担心特性之间的相关性。太复杂了,没法分析。让大联盟来解决。目的证明手段是正当的。只要我们有足够丰富的标记数据集,我们就可以对其进行划分,以训练和测试分裂,并可靠地测量我们称为“结束”的质量。
我们确实需要考虑特征空间爆炸。我们已经做了。
现在来说说学习算法。我们有很多选择。朴素贝叶斯。逻辑回归。决策树。随机森林。梯度推进。甚至可能是深度学习。浮出水面的关键点是,这些选择跨越不同的复杂程度。有些可以自动发现特别能预测情绪的多元特征。这里的风险是,他们发现的许多多元特征也是有噪声的。
好了,现在我们有许多功能选择和许多学习算法选择。潜在的非常强大。但也有风险。如前所述,我们可以通过记住特征空间爆炸来降低风险。
尽管最终我们应该专注于构建尽可能丰富的带标签的数据集,即使只是增量式的。从长远来看,这比战术优化功能更有价值,以弥补没有一个很好的训练集。这是这个问题最重要的一个方面。投资这个。
目标变量
至此,我们一直认为情感分类是一个四级问题:正,负,兼,非。在某些设置中,类和都可以忽略。在这样的设置中,我们将和都不解释为空档。
在大多数用例中,我们只关心前两个。所以中立是讨厌的职业。“讨厌”意味着它需要被考虑,即使这不是我们想要的。为什么需要对其进行说明?嗯,我们不希望中性的文本被归类为积极或消极。换句话说,包括中性类(由足够丰富的训练集支持),提高了肯定和否定的精确度。
这很容易用一个例子来说明。还记得那个例子吗
xyz phone sucks → negative
我们不会想要这样的推论手机 → 烂。意味着每部手机都很烂。通过添加中性类,以及适当丰富的训练集,这种类型的不必要的推理的风险大大降低。
概率分类
无论我们最终选择哪种学习算法——朴素贝叶斯、逻辑回归、决策树、随机森林、梯度推进等等——我们都应该考虑利用各种类别的预测概率。例如,如果一个输入的预测概率大约是 50%(正面),50%(负面),0% (0),那么我们可以将文本解释为既有正面情绪也有负面情绪。
如何高效构建训练集
好的,很明显 ML 方法是强大的。现在让我们看看“喂养野兽”,即建立一个丰富的训练集。
这里有一个想法,如何快速组装一个大型文本集,可以有效地手动标记。
- 选择一个合适的非结构化文本来源。例如电子商务网站上的产品评论。
- 在电子表格中创建两列,一列用于文本,一列用于标签。
- 将每个文档(例如每个产品评论)放在标签为文本的列中自己的单元格中。
- 手动添加标签。
让我们详细说明第 4 步。考虑一下众包。或者至少在团队成员之间分配工作。另外,采用一种约定,即 label 列中的空单元格表示一个特定的标签。一个好的选择是既不,也就是中立。
您可能会惊讶地发现,使用这个过程可以如此快速地建立一个丰富的训练集。
如果您的产品评论数据集附带了每个评论的星级,您可以使用该评级来自动标记正面和负面实例。这可以加快贴标过程。也就是说,您应该在自动标记后进行手动检查,并纠正那些错误的标签。
这种自动标记背后的假设是它的质量相当好。因此只有一小部分标签需要固定。你必须看着他们所有人。尽管如此,视觉扫描所有标签比编辑单个标签具有更高的吞吐量。
训练实例粒度
一般来说,在可能的范围内,输入实例应该更细粒度而不是更粗粒度。客户产品评论通常足够精细。尤其是如果它们已经被标记了评级,我们可以从评级中自动导出情感目标。在这种情况下,将较长的评论分解成单独的句子,并手动给它们加上适当的情感标签可能会工作量过大,而其好处尚不清楚。
接下来,考虑从更长的文档开始。比如产品类的长篇评论文章。例如,2020 年最佳 10 款手机或者2020 年最佳 10 只股票。
将它们分解成更精细的粒度,比如段落甚至句子,这种情况更为明显。显然,如果我们能够将文本限制在特定情感适用的区域,它可以帮助提高学习算法的准确性。
举一个极端的例子,假设你有一份 20 页的文件,除了一句带有强烈感情色彩的话外,其余都是中性的。把这句话贴上情绪标签,其余文字贴上中性标签,是有道理的。这可能比用一句话给 20 页的文件贴上情绪标签更有效。
字段中实例的粒度
如上所述,对于训练集,训练集中的细粒度实例通常比粗粒度实例更好。这种偏好不适用于分类时间,即分类器在现场的使用。我们应该继续前进,预测给我们的任何文本的情绪,无论是一个句子还是一章。
不像在训练期间,预测一个长文档的情绪没有负面影响。只是期望值的问题。如果用户寻找一个比一个段落更长的文档的情感,她真正的意思是她想要整个文本的总体情感。是整体正面,整体负面,两者都有,还是都没有(中性)?
这很好,有时这就是你想要的。一旦你发现了带有某种情感的文档,你就可以一直深入下去,对它们的单个句子或段落运行情感分类器。
有鉴于此,我们应该记住,对来自标记数据集的测试集的评估不会产生分类器在该领域中工作得如何的准确评估。拒不接受的测试集来自带标签的数据集,由于前面讨论的原因,该数据集由粒度实例组成。字段的输入不一定总是那么精确。
基于方面的情感分析
在这里,除了解读文本中的各种情绪,我们还试图找出它们中的哪一个适用于什么。
很明显,这种分析非常有用,如下例所示。
The camera on my <xyz-brand> phone sucks. Apart from that, I’m happy.
你明明想知道什么是被抱怨的,什么是被喜欢的。
通常,我们也关心提取实际的情感短语。考虑下面的例子,它来自对一台新电视的整体评估。
Good price. Sharp image. Vivid colors. Static in Audio. Motion lags a bit.
理想情况下,我们希望从中提取(方面,情感短语,极性)三元组。
Aspect: price image colors audio motion
Sentiment-phrase: good sharp vivid static lags a bit
Polarity: + + + — -
极性可以帮助获得总体质量分数(例如,这里是 5 分中的 3 分)。也可能有其他用途。
提取特征和情感短语
让我们通过[2]处的 postagger 运行这段文本。
回想一下,位置标签图例是
位置标签的图例
你注意到了什么?作为第一次尝试,将文本分成句子,在每个句子上运行词性标注器,如果标注序列是
认为形容词是情感短语,名词是体
将形容词视为情感短语,将名词视为体,效果出奇地好。确切地说,就是。不记得了,因为这个模式太具体了。例如,它没有检测到动作中的体貌短语有点滞后。
那么如何才能尝试延伸上一段的思路来尝试提高召回率呢?将此公式化为序列标记问题。类似问题命名实体识别的详细序列标记公式见[3]。
文本被标记为单词序列。与这个序列相关联的是一个标签序列,它指示什么是方面以及什么是情感短语。
下面是我们早先的例子,在这个惯例中被重新表述,用 A 表示体,S 表示情感短语,N 都不表示。我们把这一对一分为二,因为它不适合放在一条水平线上。
**words** Good price. Sharp image. Vivid colors. Static in Audio.
**labels ** S A S A S A S N A **words** Motionlags a bit.
**labels ** A S S S
在[3]中,我们集中于序列标记的隐马尔可夫模型。这里,更自然的是使用条件马尔可夫模型[4],原因我们将在下面解释。
一、什么是条件马尔可夫模型?回想一下,我们的推理问题是输入一个单词序列,并为它找到最可能的标签序列。对于令牌序列[ 运动,滞后,位,位,我们期望最佳标签序列为【A,S,S,S】。
条件马尔可夫模型(CMM)将这个推理问题建模为寻找标签序列 L 的问题,该标签序列最大化给定令牌序列 T 的条件概率 P ( L | T )。马尔可夫模型做了一些假设,使得这个推理问题易于处理。具体来说,P(L|T)被假设为可因式分解的
P(L|T)=P(L1 |L0,T1)**P(L2 |L1, T 2)…* P 【T39
与其解释,不如用我们的例子来说明。我们添加了一个标签 B 表示开始。
P( [B,A,S,S,S] | [B,运动,滞后, a ,位)= P(A | B,运动 )P(S|A,滞后* )P(S|S, a )P(S|S,位
我们不描述推理算法。对这篇文章来说太复杂了。另外,这不是我们关注的重点。然而,我们将定性地解释上面例子中的个体概率。有了这样的解释,我们可以想象尝试所有可能的标签序列,计算每个的概率,并找到概率最高的一个。
先说 P (A|B,动作)。这受两个因素及其相互作用的影响。首先,第一个单词是方面的一部分的可能性。二、动作是体词的可能性。
第一个因素的可能性远大于 0。我们可以想象许多第一个词是体词的真实例子。比如摄像头就是低分辨率的。
这是第二个因素的可能性,我们想详细讨论。考虑 P (A| 运动),忽略先前状态 b 的影响。CMM 允许我们将这种概率建模为受我们从 A 和运动的组合中选择的任何特征的影响。可能重叠。相比之下,HMM 将根据 P ( 运动 |A)来工作。它会把动作和 A 视为符号,不让我们利用任何我们认为有用的功能。
实际上,我们可以将 P(A| 运动)视为一个监督学习问题,其中(A,运动)是输入,P(A| 运动)是输出。这种方法的强大之处在于它能够学习复杂的映射P(LI |TI),在这些映射中,我们可以使用对( L i, T i)中我们认为合适的任何特征。
我特别想到了两个特点。单词的词性以及单词是否被标记为在可识别的命名实体中。(参见[3],它涵盖了自然语言处理中的命名实体识别,有许多真实世界的用例及方法。)
我们看到的例子已经暗示了词类特征,在这些例子中,词性标签名词似乎是标签方面的预测因子,而形容词似乎是情感短语的预测因子。命名实体特性的动机是直觉,方面通常是特定类型的对象。例如,零售产品。一个能够识别零售产品和相关产品特征的 NER,对于从充满感情色彩的评论中挑选出这些方面非常有用。
通常我们设置 NER 来识别细粒度的实体。如产品名称。不是名词短语。虽然原则上我们可以,但名词短语太多种多样,无法模仿 NER。位置标签的粒度较粗。有鉴于此,我们可以认为将两个特性结合起来的好处如下。NER 给了我们精确性。发布功能有助于回忆。
延伸阅读
- https://monkey learn . com/blog/aspect-based-opinion-analysis/
- https://parts-of-speech.info/
- https://towards data science . com/named-entity-recognition-in-NLP-be 09139 fa 7b 8
- https://en.wikipedia.org/wiki/Maximum-entropy_Markov_model
用于主题建模和聚类的文本摘要
将冗长的文本简化为简短的摘要
这是使用自然语言处理(NLP)分析医疗图表系列的第 2 部分
在第一部分中,我们谈到了清理文本和提取图表注释的部分,这可能对分析师的进一步注释有用。因此,如果他们只是寻找“过敏”或“社会历史”,减少他们手动浏览整个图表笔记的时间。
NLP 任务:
- 预处理和清洗
- 文本摘要—我们在这里
- 使用潜在狄利克雷分配(LDA)的主题建模
- 聚类
如果你想自己尝试整个代码或者跟随,请访问我在 GitHub 上发布的 jupyter 笔记本:https://GitHub . com/gaurikatyagi/Natural-Language-Processing/blob/master/introduction % 20 to % 20 NLP-Clustering % 20 text . ipynb
数据:
资料来源:https://mimic.physionet.org/about/mimic/
医生在他们的电脑上做笔记,80%的记录是没有结构的。这使得信息处理更加困难。我们不要忘记,解释医疗术语也不是一件容易的事情。它需要大量的上下文来解释。让我们看看我们有什么:
按作者分类的图像:作为输入的文本数据
文本摘要
Spacy 不擅长识别医疗保健文档的“命名实体识别”。见下文:
doc = nlp(notes_data["TEXT"][178])
text_label_df = pd.DataFrame({"label":[ent.label_ for ent in doc.ents],
"text": [ent.text for ent in doc.ents]
})
display(HTML(text_label_df.head(10).to_html()))
作者提供的图片:医疗行业术语中的词性标注表现不佳
但是,这并不意味着它不能用来总结我们的文本。使用“词性标注”来识别文本中的依存关系仍然很棒。让我们看看:
# Process the text
doc = nlp(notes_data["TEXT"][174][:100])
print(notes_data["TEXT"][174][:100], "\n")
# Iterate over the tokens in the doc
for token in doc:
if not (token.pos_ == 'DET' or token.pos_ == 'PUNCT' or token.pos_ == 'SPACE' or 'CONJ' in token.pos_):
print(token.text, token.pos_)
print("lemma:", token.lemma_)
print("dependency:", token.dep_, "- ", token.head.orth_)
print("prefix:", token.prefix_)
print("suffix:", token.suffix_)
作者图片:依赖性识别
所以,我们可以总结一下课文;基于依赖性跟踪。YAYYYYY!!!
这是总结的结果!(顺便说一句,我试图缩小我的 jupyter 笔记本以向您显示文本差异,但仍未能捕捉到完整的图表注释。我也将分别粘贴这些内容,或者您可以在 Github 页面上查看我的输出(在顶部提到)。
作者图片:摘要文本
我们如何将整个文档的要点变成简洁明了的短语,这不是很好吗?这些摘要将用于主题建模(第 3 节)和第 4 节的文档聚类。
使用编码器-解码器网络从零开始进行文本摘要
从新闻文章中总结出有意义的标题
在学生时代,我们大多数人都会遇到英语试卷中的阅读理解部分。我们会得到一段或一篇文章,根据这段或一篇文章,我们需要回答几个问题。
作为人类,我们该如何完成眼前的任务呢?我们通读整篇文章,理解提出问题的背景,然后写出答案。有没有一种方法可以让我们使用人工智能和深度学习技术来模仿我们的这种行为?
自动文本摘要是机器学习和自然语言处理中的一个常见问题。解决这个问题有两种方法。
- 摘要 -摘要文本摘要通过从原始文本中提取最重要的句子来形成最终摘要。我们做一些提取文本摘要来解决简单的阅读理解练习。TextRank 是一种非常流行的抽取式无监督文本摘要技术。
2。另一方面,抽象摘要-抽象文本摘要,是一种通过重新措辞或使用新单词生成新句子来生成摘要的技术,而不是简单地提取重要句子。例如,阅读理解中的一些问题可能不是直截了当的,在这种情况下,我们会重新措辞或使用新单词来回答这些问题。
我们人类可以很容易地完成这两种文本摘要。在这篇博客中,让我们看看如何使用深度学习技术实现抽象文本摘要。
问题陈述
给定一篇新闻文章,我们将对其进行总结,并生成适当的标题。
每当任何媒体账户在 Twitter 或任何社交网站上分享新闻故事时,他们都会提供一个简洁的标题/点击诱饵,让用户点击链接并阅读文章。
媒体公司经常提供耸人听闻的标题,作为点击诱饵。这是一种常用来增加网站点击率的技术。
我们的问题陈述是生成给定文章文本的标题。为此,我们使用 news_summary 数据集。您可以在此下载数据集
CNN 的一条推文,标题是关于 COVID 19 的一篇文章
在我们浏览代码之前,让我们学习一些构建抽象文本摘要器所需的概念。
序列对序列模型
像多层感知器(MLP)这样的技术很适合你的输入数据是矢量,如果你的输入数据是图像,卷积神经网络(CNN)也很适合。
如果我的输入 x 是一个序列呢?如果 x 是一串单词。在大多数语言中,词序很重要。我们需要以某种方式保持单词的顺序。
这里的核心思想是,如果输出依赖于输入序列,那么我们需要建立一种新型的神经网络,它重视序列信息,以某种方式保留并利用序列信息。
Google Translate 是 seq2seq 模型应用程序的一个很好的例子
我们可以对任何涉及序列信息的问题建立 Seq2Seq 模型。在我们的例子中,我们的目标是构建一个文本摘要生成器,其中输入是一长串单词(在文本正文中),输出是一个摘要(也是一个序列)。因此,我们可以将此建模为多对多 Seq2Seq 问题。
一个多对多的 seq2seq 模型有两个构件- 编码器和解码器。 编解码架构主要用于解决输入输出序列长度不同的序列对序列(Seq2Seq)问题。
通常,递归神经网络(RNNs)的变体,即门控递归神经网络(GRU)或长短期记忆(LSTM),优选作为编码器和解码器组件。这是因为它们能够通过克服梯度消失的问题来捕捉长期依赖性。
编码器-解码器架构
让我们看看编码器-解码器架构的高级概述,然后看看它在训练和推理阶段的详细工作。
直观地看,这是我们的编码器-解码器网络中发生的情况:
1.我们将输入(在我们的例子中是来自新闻文章的文本)提供给编码器单元。编码器读取输入序列,并在称为内部状态向量的东西中总结信息(在 LSTM 的情况下,这些被称为隐藏状态和单元状态向量)。
2.编码器生成称为上下文向量的东西,作为输入传递给解码器单元。编码器生成的输出被丢弃,只有上下文向量被传递给解码器。
3.解码器单元基于上下文向量生成输出序列。
我们可以分两个阶段设置编码器-解码器:
- 培训阶段
- 推理阶段
培训阶段
A.编码器
在每一个时间步的训练阶段,我们从一个句子中一个接一个地按顺序输入单词给编码器。例如,如果有一个句子“我是一个好孩子”,那么在时间步 t=1,单词 I 被馈送,然后在时间步 t=2,单词 am 被馈送,等等。
比方说,我们有一个由字 x1、x2、x3、x4 组成的序列 x,那么训练阶段的编码器如下所示:
培训阶段
LSTM 单元的初始状态是零矢量或者它是随机初始化的。现在 h1,c1 是当序列 x 的字 x1 作为输入被馈送时,在时间步长 t=1 的 LSTM 单元的状态。
类似地,h2、c2 是当序列 x 的字 x2 作为输入被馈送时,LSTM 单元在时间步长 t=2 的状态,等等。
最后一个时间步长的隐藏状态(hi)和单元状态(ci)用于初始化解码器。
B.解码器
现在,解码器的初始状态被初始化为编码器的最终状态。这直观地意味着解码器被训练成根据编码器编码的信息开始生成输出序列。
<开始> 和< 结束> 是在将目标序列馈送到解码器之前添加到目标序列(在我们的例子中是我们想要预测的标题)的特殊标记。
解码测试序列时目标序列未知。因此,我们通过将第一个字发送到解码器来开始预测目标序列,该第一个字通常是<开始>标记。而< end > 记号表示句子结束。
解码器架构
推理阶段
现在在推断阶段,我们希望我们的解码器预测我们的输出序列(在我们的例子中是标题)。训练后,在目标序列未知的新源序列上测试该模型。因此,我们需要建立推理架构来解码一个测试序列
推理阶段
在每个时间步长,我的解码器中的 LSTM 单元给我输出 y,y,y …y^k.,其中 k 是输出序列的长度。在时间步长 t=1 时,产生输出 y,在时间步长 t=2 时,产生输出 y ^2,依此类推。
但是在前面提到的测试阶段,我们不知道目标序列的长度。我们如何解决这个问题?或者换句话说,我们如何解码测试序列。同样,我们遵循以下步骤:
- 对整个输入序列进行编码,并用编码器的内部状态初始化解码器
- 传递<开始>令牌作为解码器的输入
- 用内部状态运行解码器一个时间步长
- 输出将是下一个单词的概率。将选择具有最大概率的单词。
- 在下一个时间步长将采样字作为输入传递给解码器,并用当前时间步长更新内部状态
- 重复步骤 3–5,直到我们生成<end>令牌或达到目标序列的最大长度。
编码器-解码器网络的缺点
- 在编码器-解码器网络中,编码器生成一个上下文向量,作为输入传递给解码器。现在,如果我们的输入序列很大(在我们的例子中,来自新闻文章的文本大部分都很大),一个单独的上下文向量就不能捕捉输入序列的本质。
- 编码器很难将长序列记忆到固定长度的向量中
- 。双语评价替角得分,简称 BLEU ,是一个将生成的句子评价为参考句子的度量标准。完全匹配导致 1.0 的分数,而完全不匹配导致 0.0 的分数。
研究人员观察到,随着源文本和参考文本的句子长度增加,BLEU 分数会下降。它的表现还算不错——直到句子长度达到 20,之后分数就会下降。
对于我们的任务,源和目标句子长度都大于 20,因此我们需要克服编码器-解码器网络的这一缺点。
注意力的概念
- 当人们阅读任何冗长的段落时,他们会注意某些单词,然后他们会将注意力转移到接下来的几个单词上,以此类推。
- 直觉地想到你的老师在你的历史试卷上批改你的 10 分答案。老师会带着一把钥匙,钥匙上有一些重要的要点/单词。所以在你的答题纸中,你的老师会在答案中寻找这些重要的单词。更注重的是关键词。
- 因此,当给定一个冗长的输入序列时,人类将注意力从一个单词序列转移到另一个单词序列
- 因此,我们可以增加源序列中产生目标序列的特定部分的重要性,而不是查看源序列中的所有单词。这是注意力机制背后的基本思想。
- 注意机制利用双向 RNN。常规 RNN 是单向的,因为序列是从第一个单词到最后一个单词处理的。在双向 RNN 中,我们将有一个正向和反向连接。
- 所以除了前向连接,每个神经元还有一个后向连接。
- 从神经元的前向和后向连接产生的输出被连接在一起,以给出输出 y1,y2and 等等。因此,我们将有两个反向传播,一个用于反向的正向路径,另一个用于正向的反向路径。
- 上下文向量只不过是编码器输出的加权和。
注意机制
根据所关注的上下文向量的导出方式,存在 2 种不同类别的注意机制:
- 全局注意力-在这里,注意力被放在所有的源位置上。换句话说,编码器的所有隐藏状态被考虑用于导出关注上下文向量:
- 局部注意力——在这里,注意力只放在少数几个源位置上。仅考虑编码器的几个隐藏状态来导出关注上下文向量。
我们将利用全球的注意力来完成手头的任务。
代码走查
现在我们已经学习了所有的概念,让我们深入研究代码。首先,让我们导入所有必需的库
自定义关注层
Keras 没有正式支持关注层。因此,我们既可以实现我们的注意力层,也可以使用第三方实现。对于这个博客,我们将选择后者。你可以在这里 从T5 下载注意力层,并将其复制到另一个名为 attention.py 的文件中,然后我们可以导入相同的文件。
现在让我们阅读我们的数据集。由于计算的限制,我们将只从数据集中加载 20000 行。
阅读我们的数据集-我们可以看到标题和新闻文章文本对。
文本预处理
现在我们需要清理我们的文本,我们对文本和标题对执行以下步骤:
- 删除多余的空格
- 扩大收缩
- 删除特殊字符
- 小写所有文本
我们使用以下函数来扩展收缩
我们使用下面的函数预处理文本和标题对:
上面的代码片段对文章文本进行预处理。同样的代码也可以用于标题列。
在这里,正文是我们的来源,标题是我们的目标。我们需要添加开始和结束标记到我们的目标序列,即我们前面看到的标题。
为标题添加开始和结束标记(sostok 和 eostok)。
现在我们将添加一个新的功能字数。我们将为文本和标题添加此功能。然后让我们看看文字和标题的字数百分比。这将有助于我们对文本长度的分布有一个整体的了解。这将帮助我们确定序列的最大长度
让我们得到文本字数的百分位值。
文本字数的百分比值
让我们看看文本字数从 90 到 100 的百分位值
文本字数的 90-100%值
我们将第 95 个百分位数即 62 作为我们的最大文本长度。类似地,我们绘制了标题的百分位值,并将 15 作为标题的最大长度。
文本和标题的最大长度
测试列车拆分
在继续构建我们的模型之前,我们需要对我们的数据进行测试训练分割。我们使用 sklearn 来做同样的事情。我们将使用 70 %的数据作为训练数据,并在剩余的 30 %上评估性能。
列车测试分离
用于文本的预训练单词嵌入
我们之前看到,我们在每个时间步向编码器发送文本序列。来自输入序列的字在每个时间步长被传递到编码器网络。在它作为输入被传递到编码器单元之前,我们添加一个嵌入层。
什么是单词嵌入?
一个嵌入是一个相对低维的空间,你可以将高维向量转化到其中。嵌入使得在大量输入上进行机器学习变得更加容易,比如表示单词的稀疏向量。理想情况下,嵌入通过在嵌入空间中将语义相似的输入放在一起来捕获输入的一些语义。嵌入可以跨模型学习和重用。
来源-【analyticsvidhya.com
例如,“椰子”和“北极熊”是语义完全不同的词,所以合理的嵌入空间应该将它们表示为相距很远的向量。但是“厨房”和“晚餐”是有关联的词,所以它们应该紧密地嵌在一起。
通过将降维技术应用于文本语料库中单词之间的共现统计数据集来计算单词嵌入。这可以通过神经网络(“word2vec”技术)或矩阵分解来完成。
手套嵌入
最普遍使用的嵌入技术之一是手套嵌入。GloVe 代表“单词表示的全局向量”。我们将使用手套嵌入我们的输入文本序列(你可以在这里阅读手套)。
具体来说,我们将使用在 2014 年英语维基百科转储上计算的 40 万个单词的 100 维手套嵌入。你可以在这里下载它们
现在我们需要检查手套语料库中有多少来自输入文本序列语料库的单词。这非常重要,因为我们的模型将只学习手套语料库中的单词。我们可以使用下面的代码来检查这一点:
我们观察到在手套向量和我们的语料库中出现的单词的百分比接近 73%,这是非常好的。
标记化
任何机器学习或深度学习模型都无法直接理解文本。我们需要把它转换成数字。我们可以通过标记化来做到这一点。
标记化是将文本分割成一组有意义的片段的过程。这些东西被称为令牌。比如,我们可以把一段文字分成单词,也可以分成句子。记号赋予器构建词汇表并将单词序列转换成整数序列。
我们对文本和标题进行标记,然后将其转换为整数,然后填充到最大长度(我们在上一节中已经达到了这个最大长度)
接下来,让我们构建字典,将索引转换为目标和源词汇的单词
在前面的部分中,我们已经加载了手套模型。现在让我们从手套模型中获得文本序列的权重矩阵。在手套嵌入中没有找到的字将被初始化为零向量。我们可以使用下面这段代码来获得权重矩阵:
构建我们的模型
我们通过垂直堆叠 3 个 LSTMs 来构建编码器,水平轴是时间轴。堆叠有助于增加模型的复杂性,也有助于更好地表示我们的输入文本序列。
堆叠的 LSTM 模型看起来会像这样:
(我们仅对编码器架构使用堆栈,不对解码器使用堆栈,如下图所示。)
堆叠架构(图片来源-https://towardsdatascience . com/time-series-forecasting-with-deep-stacked-单向-双向-lstms-de7c099bd918 )
我们可以使用下面的代码来构建我们的模型:
一些重要术语
- Return Sequences = True: 当 Return Sequences 参数设置为 True 时,LSTM 为每个时间步长产生隐藏状态和单元格状态
- 返回状态=真:当返回状态= 真时,LSTM 只产生最后一个时间步长的隐藏状态和单元格状态
- 初始状态:用于初始化第一个时间步长的 LSTM 内部状态
- latent_dim :表示隐藏单元的数量。
让我们看看我们的模型摘要:
模型摘要
现在我们看到有不可训练的参数。这些参数来自我们的嵌入矩阵的权重向量。我们已经将可训练设置为假,因此我们的模型不会学习这些权重。
我们现在将继续编译我们的模型
现在让我们检查一下我们的模型并保存最好的权重。此外,让我们用 3 的耐心为我们的模型定义早期停止(这意味着如果验证损失在 3 个时期后没有减少,则训练停止)
现在让我们用 128 的批量训练我们的模型
训练我们的模型
我们绘制了纪元与价值损失图
纪元 vs 价值损失
我们观察到 val 损失在第 18 个时期后没有减少,因此我们停止了训练。
现在,我们为我们的模型构建推理,如前面几节所讨论的
现在我们在下面定义一个函数,它是推理过程(解码测试序列)的实现
现在让我们定义函数,将文本和标题的整数序列转换为单词序列:
最后,我们有预测功能,让我们看看我们的模型如何执行!
以下是我们的模型做出的一些预测。预测可能不是很好,因为我们已经在更少的数据点(大约 15 K)上训练了我们的模型。
即使预测的和实际的标题在文字上不完全匹配,但是传达了相同的意思。这是我们在非常小的数据集上训练我们的模型所得到的相当不错的结果。谷歌/脸书/亚马逊在千兆/万亿字节的数据上训练模型:)。
未来的工作
我们仍然可以通过以下方式在很大程度上改进我们的模型
- 增加数据集的大小-由于计算限制,我们取了大约 20k 个点。如果我们有高端 GPU 可用,这个模型可以在更大的数据集上训练,从而改善结果。
- 使用 BERT 嵌入 — BERT(来自 Transformers 的双向编码器表示)是一种最先进的语言表示模型。我们可以使用 BERT 为我们的文本序列获得上下文化嵌入。我们还可以使用 BERT 句子片段标记器来标记我们的输入文本序列。尽管 BERT 确实需要大量的计算能力。
- 双向 lstm 的使用- 我们之前讨论过,对于编码器解码器,我们注意使用双向 lstm。然而,我们用单向 LSTM 构建了我们的模型。双向 LSTM 能够从两个方向捕获上下文,并产生更好的上下文向量。
- 尝试使用波束搜索来解码序列
- 根据 BLEU 分数评估您的模型的性能。
参考
- AAIC
- www.analyticsvidhya.com
- https://arxiv.org/abs/1706.03762
- https://towards data science . com/text-summarying-using-deep-learning-6e 379 ed 2e 89 c
- https://www . analyticsvidhya . com/blog/2019/06/comprehensive-guide-text-summarying-using-deep-learning-python/
使用深度神经网络的文本摘要
基于 seq2seq 模型的文摘综述
简介
每天产生的文本数据量在复杂性和数量上都在快速增长。社交媒体、新闻文章、电子邮件、短信(清单还在继续..),产生海量信息,浏览冗长的文字材料变得繁琐(也很无聊!).谢天谢地,随着深度学习的进步,我们可以建立模型来缩短长文本,并产生清晰连贯的摘要,以节省时间并有效地理解关键点。
我们可以将文本摘要大致分为两种类型:
1。提取摘要:这种技术包括从输入的句子中提取重要的单词/短语。基本思想是通过从输入句子中选择最重要的单词来创建摘要
2。抽象概括:这种技术包括生成全新的短语来捕捉输入句子的意思。潜在的想法是把重点放在形式上——旨在生成语法摘要,因此需要高级语言建模技术。
在本文中,我们将使用 PyTorch 建立一个序列 2 序列(编码器-解码器)模型,使用 GRU 进行简单的点积注意力,并评估他们的注意力得分。我们将进一步研究像 BLEU、ROUGE 这样的指标来评估我们的模型。
使用的数据集: 我们将使用 wikihow 数据集,该数据集包含大约 200,000 对长序列文章及其标题。该数据集是可用于摘要的大规模数据集之一,文章的长度变化很大。这些文章在写作风格上非常多样化,这使得摘要问题更具挑战性和趣味性。
关于数据集的更多信息:https://arxiv.org/abs/1810.09305
数据预处理
预处理和清理是一个重要的步骤,因为在不干净和混乱的数据上建立模型会反过来产生混乱的结果。在将数据输入模型之前,我们将应用以下清理技术:
- 将所有文本转换为小写以便进一步处理
- 解析 HTML 标签
- 正在删除()和[]之间的文本
- 缩略映射—替换单词的缩略版本(例如,不能被替换为不能等等)
- 删除撇号
- 删除标点符号和特殊字符
- 使用 nltk 库删除停用词
- 仅保留长单词,即长度大于 3 的单词
我们将首先以字典的形式定义缩写:
## Find the complete list of contractions on my Github Repocontraction_mapping = {"ain't": "is not", "aren't": "are not"}
stop_words = set(stopwords.words('english'))**def** text_cleaner(text,num): str = text.lower()
str = BeautifulSoup(str, "lxml").text
str = re.sub("[\(\[].*?[\)\]]", "", str)
str = ' '.join([contraction_mapping[t] **if** t **in** contraction_mapping **else** t **for** t **in** str.split(" ")])
str = re.sub(r"'s\b","",str)
str = re.sub("[^a-zA-Z]", " ", str)
**if**(num==0):
tokens = [w **for** w **in** str.split() **if** **not** w **in** stop_words]
**else**:
tokens=str.split() long_words=[]
**for** i **in** tokens:
**if** len(i)>3: *#removing short words*
long_words.append(i)
**return** (" ".join(long_words)).strip()*#calling the above function*
clean_text = []
clean_summary = []**for** t **in** df['text']:
clean_text.append(text_cleaner(t,0))**for** t **in** df['headline']:
clean_summary.append(text_cleaner(t,0))
深度模型设计
在将训练数据输入我们的深度模型之前,我们将每个单词表示为一个热点向量。然后,我们将需要每个单词的唯一索引,以将其用作网络的输入和输出。
为此,我们将创建一个助手类 Lang ,它具有单词- >索引和索引- >单词映射以及每个单词的计数。
为了读取模型中的数据,我们以列表的形式创建了输入和输出对(Pairs- > [Input,Output])
Seq2seq 模型,注意使用 GRU 和教师强制
我们将使用 seq2seq(编码器-解码器架构)模型,关注简单的点积。选择这种架构的基本思想是,我们手头有一个多对多的问题(n 个单词作为输入,m 个单词作为输出)。下图显示了该模型的详细架构图。
作者图片
该架构中有四个主要组件:
编码器:seq 2 seq 模型的编码器层从输入文本中提取信息,并将其编码成单个向量,即上下文向量。
基本上,对于每个输入单词,编码器都会生成一个隐藏状态和一个向量,将这个隐藏状态用于下一个输入单词。我们使用 GRU(门控递归单元) 用于编码器层,以便捕捉长期依赖性——减轻使用普通 RNNs 时遇到的消失/爆炸梯度问题。
GRU 单元一次读取一个字,并使用更新和复位门,计算隐藏状态内容和单元状态。我发现下面两个链接对获得更多关于 GRU 工作的信息很有用:
门控递归神经网络序列建模的经验评估
门控递归单元(GRU)解码器:seq 2 seq 模型的解码器层使用编码器最后的隐藏状态即上下文向量,生成输出字。一旦句子被编码,解码过程就开始,并且在每个步骤/时间,解码器被给予隐藏状态和输入令牌。
在初始时间戳/状态,第一隐藏状态是上下文向量,输入向量是 SOS(字符串开始)。当到达 EOS(句子结束)时,解码过程结束。
SOS 和 EOS 标记分别显式添加在每个句子的开头和结尾。注意机制: 利用编码器-解码器架构,将编码后的上下文向量传递给解码器,生成输出句子。现在如果输入的句子很长,单个上下文向量无法捕捉所有重要信息怎么办。 这就是注意力进入画面的地方!!!
使用注意力的主要直觉是让模型聚焦/注意输入文本最重要的部分。塞翁失马,焉知非福,也有助于克服消失渐变问题。
注意力有不同的类型——加法、乘法,但是,我们将使用基本的点积注意力作为我们的模型。1。注意力分数首先通过计算编码器(h)和解码器(s)隐藏状态
2 的点积来计算。这些注意力分数通过 Softmax 层转换成分布(α) 。
3。然后计算隐藏状态(z) 的加权和。
4。该 z 然后与 s 连接,并通过 softmax 层使用“贪婪算法”(通过计算 argmax)生成单词在这种架构中,我们不是直接使用最后一个编码器隐藏状态的输出,而是提供所有编码器隐藏状态的加权组合。这有助于模型关注长序列中的重要单词。
支持方程
教师强制: 一般来说,对于递归神经网络,一个状态的输出作为输入馈入下一个状态。这个过程导致收敛缓慢,从而增加了训练时间。
什么是老师强制 老师强制通过向模型馈送实际值/地面真实值来解决这个收敛缓慢的问题。这种技术背后的基本直觉是,不是将解码器预测的输出作为下一个状态的输入,而是将基本事实或实际值馈送给模型。如果该模型预测了一个错误的单词,则可能导致所有被预测的其他单词都不正确的情况。因此,如果模型预测/生成了错误的单词,教师强制输入实际值,从而校正模型。
教师强制是一种快速有效的训练 RNNs 的方法,然而,当生成的序列与训练过程中看到的不同时,这种方法可能导致 更脆弱/不稳定的模型。
为了处理此类问题,我们将采用一种方法,随机选择使用地面实况输出或前一时间步生成的输出作为当前时间步的输入。下面的代码片段展示了教师强制的动态实现
teacher_forcing_ratio = 0.5use_teacher_forcing = **True** **if** random.random() < teacher_forcing_ratio **else** **False**
**if** use_teacher_forcing:
*# Teacher forcing: Feed the target as the next input*
**for** di **in** range(target_length):
decoder_output, decoder_hidden, decoder_attention = decoder(
decoder_input, decoder_hidden, encoder_outputs)
loss += criterion(decoder_output, target_tensor[di])
decoder_input = target_tensor[di] *# Teacher forcing*
**else**:
*# W/O teacher forcing: use own predictions as the next input*
**for** di **in** range(target_length):
decoder_output, decoder_hidden, decoder_attention = decoder(
decoder_input, decoder_hidden, encoder_outputs)
topv, topi = decoder_output.topk(1)
decoder_input = topi.squeeze().detach() *# detach from history as input*
loss += criterion(decoder_output, target_tensor[di])
**if** decoder_input.item() == EOS_token:
**break**
模型评估指标
对于我们的文本摘要问题,可以有多个正确的答案,因为我们没有一个正确的输出,所以我们可以使用不同的参数来评估我们的模型,如召回率、精确度、F 分数。下面是我们将使用的一些指标:
BLEU(双语评估替角):该度量的基础是精度,其值在[0,1]之间,其中 1 表示完全匹配,0 表示完全不匹配。这个度量基本上是通过比较作为参考句子的一部分的机器生成的单词的数量与机器生成的输出中的单词总数来计算的。让我们借助一个例子来理解这一点:
参考句子:门被锁上
机器输出:The
BLEU Score =1
上面的机器输出是一个极端的情况,但是,为了克服这个问题,BLEU 分数是以这样一种方式计算的,即生成的句子中的每个单词将被 剪切 到该单词在参考句子中出现的次数。
这基本上确保了如果一个单词出现的次数比它在参考句子中出现的次数多——在评估相似度时不会被考虑。
应用上述规则后,我们得到修改后的 BLEU 分数为 1/3
我们再来看看另一个极端的例子:
参考句子:门被锁上
机器输出: BLEU 得分: 1(即使应用以上规则)
在这种情况下,在生成的句子的长度小于参考句子的情况下,引入 简洁罚分(BP) ,即,如果生成的句子小于参考句子,则对 BLEU 分数有罚分,然而,当生成的句子长于参考句子时,不引入罚分。
简洁罚分定义为-
其中,
r 是有效参考语料长度
c 是生成/候选句子的长度
这个指标是由 Kishore Papineni 在 2002 年首次提出的。有关此指标的更多详细信息,请参考以下链接:
BLEU:一种自动评估机器翻译的方法
ROUGE(面向回忆的理解,用于 Gisting 评估):
ROUGE 基本上是一种面向回忆的措施,它通过比较参考句子中机器生成的单词数量与参考句子中的总单词数量来工作。
这个指标更直观,因为每次我们向池中添加一个引用,我们就扩展了交替摘要的空间。因此,当我们有多个引用时,应该首选这个指标。
ROUGE 的实现与 BELU 非常相似,但是,还有其他底层实现,如 LCS(最长公共子序列)和 skip-gram 等。我们可以使用 python 库 ROUGE 直接使用 ROUGE-N 实现。
更多的细节,你可以参考下面的文章:
ROUGE:一个自动评估摘要的包
我们已经看到了基于精度的指标(BLEU)和面向召回的指标(ROUGE),但是,如果我们希望我们的评估基于召回和精度,我们可以使用 F-Score 作为评估度量。
结果:实现代码可以在我的 Github 上找到。
该模型在 Google Colab Pro(T4 和 P100 GPU - 25GB 高内存虚拟机)上运行了约 6-7 个小时,它似乎在较短的摘要(~50 个单词)上运行良好。然而,可以通过 进一步调整超参数 (学习率、优化器、损失函数、隐藏层、动量、迭代等)来优化模型。)
下一步…
- 双向/堆叠 GRU 单元可用于提高性能
- 实施不同类型的注意机制——倍增注意、多头注意等。
- 可以使用波束搜索而不是贪婪算法来选择输出字
参考
- 注意力是你所需要的一切
- https://py torch . org/tutorials/intermediate/seq 2 seq _ translation _ tutorial . html
- *【https://www.aclweb.org/anthology/W04-1013.pdf *
具有手套嵌入的文本摘要..
基于注意力的 LSTM 编解码器模型的手套嵌入文本摘要。
安妮·斯普拉特在 Unsplash 上的照片
这个故事是我们之前的博客文章的延续,在那里我们已经讨论了文本摘要的基础,各种方法以及我们如何实现一个编码器-解码器模型(注意)来解决手头的问题。
简单回顾一下,文本摘要是从多种文本资源(如书籍、新闻文章、博客帖子、研究论文、电子邮件和推文)中生成简明而有意义的文本摘要的过程。
我们已经看到编码器-解码器(seqtoseq)模型是摘要任务的完美选择,所以我们将继续使用该架构。除此之外,我们将使用 GloVe 预训练单词嵌入让我们的模型领先一步,并检查它在理解语言语义和总结方面是否真的表现得更好。
什么是单词嵌入?
“单词嵌入”是一组旨在将语义映射到几何空间的自然语言处理技术。这是通过将数字向量与字典中的每个单词相关联来实现的,使得任何两个向量之间的距离(例如,L2 距离或更常见的余弦距离)将捕获两个相关单词之间的部分语义关系。这些向量形成的几何空间称为嵌入空间。
例如,“ 椰子 ”和“ 北极熊” 是语义完全不同的单词,因此合理的嵌入空间会将它们表示为相距很远的向量。但是“ 厨房 ”和“ 晚餐 ”是关联词,所以应该是嵌得比较近的。
理想情况下,在一个好的嵌入空间中,从“厨房”到“晚餐”的“路径”(一个向量)将精确地捕捉这两个概念之间的语义关系。在这种情况下,关系是“x 出现的地方”,因此您会期望向量kitchen - dinner
(两个嵌入向量的差,即从晚餐到厨房的路径)捕捉这种“x 出现的地方”关系。基本上,我们应该有向量恒等式:dinner + (where x occurs) = kitchen
(至少近似)。如果事实确实如此,那么我们可以用这样一个关系向量来回答问题。例如,从一个新的向量开始,例如“工作”,并应用这个关系向量,我们应该得到一些有意义的东西,例如work + (where x occurs) = office
,回答“工作发生在哪里?”。
通过对文本语料库中单词之间的共现统计数据集应用降维技术来计算单词嵌入。这可以通过神经网络(“word2vec”技术)或矩阵分解来完成。
我们可以玩这个漂亮的张量流投影仪,更好地理解单词嵌入。
所有与厨房相关的单词。
为什么要嵌入手套?
两个最常见的单词嵌入是: Word2Vec 和 GloVe,而且这两个都同样受欢迎。但是 GloVe (单词表示的全局向量)顾名思义更适合保存全局上下文,因为它通过估计给定单词与其他单词共现的概率来创建全局共现矩阵。在这里,为了进行总结,全局环境是必要的,所以我们继续使用 GloVe,但是在大多数用例中,两者之间很少有选择。
具体来说,我们将使用在 2014 年英语维基百科转储上计算的 40 万个单词的 100 维手套嵌入。你可以在这里下载它们(警告:点击这个链接将开始一个 822MB 的下载)。
编码时间:
照片由 Arian Darvishi 在 Unsplash 上拍摄
这是 LSTM 编码器-解码器模型的完整代码,添加了注意力和手套嵌入。
我们不讨论模型架构的细节,因为我们在之前的博客文章中已经讨论过了,并转而关注于向其添加手套嵌入和评估性能。
下载并解压手套
首先,让我们下载并解压缩手套嵌入。
!wget 'http://nlp.stanford.edu/data/glove.6B.zip'!unzip '/content/glove.6B.zip'
制备嵌入层
接下来,我们通过解析预训练嵌入的数据转储来计算将单词映射到已知嵌入的索引:
embeddings_index = {}
f = open(os.path.join(GLOVE_DIR, 'glove.6B.100d.txt'))
for line in f:
values = line.split()
word = values[0]
coefs = np.asarray(values[1:], dtype='float32')
embeddings_index[word] = coefs
f.close()
print('Found %s word vectors.' % len(embeddings_index))
此时,我们可以利用我们的embedding_index
字典和word_index
来计算我们的嵌入矩阵:
EMBEDDING_DIM = 100
embedding_matrix = np.zeros((len(word_index) + 1, EMBEDDING_DIM))
for word, i in word_index.items():
embedding_vector = embeddings_index.get(word)
if embedding_vector is not None:
# words not found in embedding index will be all-zeros.
embedding_matrix[i] = embedding_vector
我们将这个嵌入矩阵加载到一个Embedding
层中。注意,我们设置trainable=False
是为了防止权重在训练过程中被更新。以前我们使用默认的Embedding
层,带有trainable=True
,它通过训练过程学习嵌入。
embedding_layer = Embedding(len(word_index) + 1,
EMBEDDING_DIM,
weights=[embedding_matrix],
input_length=MAX_SEQUENCE_LENGTH,
trainable=False)
这里,我们使用 100 维手套嵌入,嵌入保存在 glove.6B.100d.txt 中。
一个Embedding
层应该被输入整数序列,即形状(samples, indices)
的 2D 输入。这些输入序列应该被填充,以便它们在一批输入数据中具有相同的长度(尽管如果您没有向层传递一个明确的input_length
参数,Embedding
层能够处理不同长度的序列)。
Embedding
层所做的只是将整数输入映射到嵌入矩阵中相应索引处的向量,即序列[1, 2]
将被转换为[embeddings[1], embeddings[2]]
。这意味着Embedding
层的输出将是一个形状为(samples, sequence_length, embedding_dim)
的 3D 张量。
培训绩效:
培训和测试损失
我们得到大约 2.25 的验证损失,这与我们在没有手套嵌入的情况下得到的损失相似。
测试:
**Review**: tried several tassimo discs gevalia best coffee ever tasted coffee snob love coffee
**Original summary:** great coffee
**Predicted summary:** best coffee ever**Review**: almonds unbelievable amount salt love salt top bought brand stores never salty maybe machine went wacko processing cannot eat unless take time try wipe salt talks forever
**Original summary:** too salty to eat
**Predicted summary:** not the best**Review:** sure much nourishment hair getting shampoo dandruff nice job cleaning hair leaving tangle free somewhat conditioned using pantene pro shampoo compares favorably pantene like way bottle made easy open dispensing far like way hair coming shampooing seem little better started used bottle seen little improvement really much improvement would say shampoo would worth trying nice job cleaning smells nice bottle easy use relatively inexpensive keep mind make many shampoos one person likes another hate
**Original summary**: seems like an excellent shampoo
**Predicted summary:** great for hair
从上面的例子中我们可以看到,它在大多数用例中表现得相当好。
这里是完整实现的 colab-notebook 。
使用手套嵌入真的能提高性能吗?
长话短说,号
虽然该模型可能在某些情况下产生更好的摘要,但不能肯定它在所有示例中都有更好的结果。这背后的主要原因是我们在这里有足够的记录(准确地说是 100000 条)供我们在编码器-解码器模型中的嵌入层学习语言的语义,所以即使没有预先训练的嵌入,它也表现得相当好。
那么,我们什么时候应该使用预先训练好的嵌入呢?
如果我们有一个短的数据集,预训练模型(如 GloVe)可以在结果上有很大的改进。如果数据集中的记录数量较少,嵌入层将无法生成自己的正确嵌入,在这种情况下,使用预先训练的嵌入将提高性能准确性。
结论
在这里,我们已经看到了如何将预训练嵌入添加到现有的 LSTM 编码器-解码器架构中,虽然结果没有出现太多尖峰,但它可以在较小的数据集上表现出色。
如果我们能够利用迁移学习的真正精髓,通过使用预训练语言模型的状态,如:,巴特, T5 ,我们可以进一步大幅改进总结过程。在我们的下一个故事中,我们将深入研究这些模型的细节,以及它们如何精彩地概括文本。
参考
- https://www.kaggle.com/snap/amazon-fine-food-reviews
- https://blog . keras . io/using-pre-trained-word-embedding-in-a-keras-model . html
- https://projector.tensorflow.org/
使用 Tacotron-2 进行文本到语音转换,使用 ESPnet 进行快速语音转换。
端到端神经文本到语音的初学者指南..
迈克尔·马森在 Unsplash 上的照片
顾名思义,文本到语音(TTS)朗读文本。它将书面文字作为输入,并将其转换为音频。TTS 可以帮助那些不想花力气去阅读一本书、博客或文章的人。在本文中,我们将看到如何在我们对 TTS 一无所知的情况下创建一个 TTS 引擎。
文本到语音体系结构
我们的 TTS 架构
上图是我们将要遵循的架构的简单表示。我们将详细研究每一个组件,我们将使用 ESPnet 框架来实现目的。
前端
我们的前端。
它主要有三个组成部分:
- 词性标注器:对输入文本进行词性标注。
- 分词:将句子分词。
- 发音:根据发音将输入文本分解成音素。例如你好,你好→ HH AH0 L OW,HH AW1 AA1 R Y UW1。这是通过一个字素到音素的转换器来完成的,在这种情况下,我们使用一个神经预训练的 G2P(字素到音素)模型。该模型旨在将英语字素(拼写)转换为音素(发音)。为了简单地说明这个 G2P 模型的工作,我们可以说,如果我们想知道某个单词的发音,它会查阅字典,如果该单词没有存储在字典中,它会使用基于 TensorFlow 的 seq2seq 模型来预测音素。
序列到序列回归器:
序列-2-序列回归器。
我们将使用预先训练的 seq-to-seq 回归器,它输入语言特征(音素)并输出声学特征(Mel-spectrogram)。在这里,我们将使用 Tacotron-2(谷歌的)和 Fastspeech(脸书的)进行操作。因此,让我们快速了解一下这两种情况:
Tacotron-2
Tacotron-2 架构。图片来源。
Tacotron 是一个人工智能驱动的语音合成系统,可以将文本转换为语音。Tacotron 2 的神经网络架构直接从文本中合成语音。它基于卷积神经网络(CNN)和递归神经网络(RNN)的组合来工作。
快速演讲
FastSpeech 的整体架构。(a)前馈变压器。(b)前馈变压器块。长度调节器。持续时间预测值。MSE 损失表示预测持续时间和提取持续时间之间的损失,它只存在于训练过程中。图片来源。
(a)、(b)前馈变压器:
FastSpeech 采用了新颖的前馈变压器结构,抛弃了常规的编码器-注意力-解码器框架,如上图所示。前馈变压器的主要组件是前馈变压器模块(FFT 模块,如图(b)所示),由自关注和 1D 卷积组成。FFT 块用于从音素序列到 mel 谱图序列的转换,在音素侧和 mel 谱图侧分别有 N 个堆叠块。独特的是,在它们之间有一个长度调节器,用于桥接音素和 Mel-频谱图序列之间的长度不匹配。(注意:音素是语音的微小而独特的声音。)
(c)长度调节器:
该型号的长度调节器如上图所示。由于音素序列的长度小于 mel 谱图序列的长度,所以一个音素对应于几个 mel 谱图。对准一个音素的 mel 频谱图的数量被称为音素持续时间。长度调节器根据持续时间扩展隐藏的音素序列,以便匹配 mel 谱图序列的长度。我们可以按比例增加或减少音素持续时间来调整语速,也可以改变空白标记的持续时间来调整单词之间的间隔,以便控制部分韵律。
(d)持续时间预测值:
持续时间预测器对于长度调节器能够确定每个音素的持续时间非常关键。如上图所示,持续时间预测器由两层 1D 卷积和一个线性层组成,用于预测持续时间。持续时间预测器堆叠在音素侧的 FFT 块上,并且通过均方误差(MSE)损失函数与 FastSpeech 联合训练。音素持续时间的标签是从自回归教师模型中编码器和解码器之间的注意力对准中提取的。
波形发生器/声码器:
声码器
我们将使用预先训练的序列到序列模型,该模型输入声学特征(Mel 频谱图)并输出波形(音频)。这里我们将使用并行 WaveGAN 声码器。这里一个 生成对抗网络 ( 【甘) 架构用于从 Mel-spectrograms 生成波形,关于这个架构的更多信息可以在这里找到。
履行
我们已经使用ESPnet框架实现了上述架构。它提供了一个惊人的结构来轻松实现上述所有预训练的模型,并集成它们。这是完整的文本到语音转换实现的笔记本。
结论
我们使用 Tacotron-2、Fastspeech、Parallel WaveGAN 等各种预训练模型实现了一个神经 TTS 系统。我们可以进一步尝试其他可能产生更好结果的模型。
参考
- https://github.com/kan-bayashi/ParallelWaveGAN
- https://github.com/espnet/espnet
- https://www . Microsoft . com/en-us/research/blog/fast speech-new-text-to-speech-model-improves-on-speed-accuracy-and-control ability/
- https://ai . Google blog . com/2017/12/taco tron-2-generating-human-like-speech . html
- http://media . speech . zone/images/interspeech 2017 _ tutorial _ Merlin _ for _ publication _ watered _ compressed _ v2 . pdf
- https://arxiv.org/pdf/1910.11480.pdf
文本矢量化:单词包(BoW)
如何将文本特征转换成矢量
图片由阿玛多·洛雷罗拍摄,来自 Unsplash
文本数据用于自然语言处理(NLP),它使用自然语言在人类和机器之间进行交互。文本数据有助于分析电影评论、使用亚马逊评论的产品等。但这里出现的问题是在建立机器学习模型时,如何处理文本数据?
通过各种技术将文本数据转换成实值向量。一种这样的方法是单词包(BoW),,这将在本文中讨论。但是为什么我们要把文本转换成矢量呢?为什么不能用文本数据建立机器学习模型?
需要文本矢量化
假设我们有一个产品的评论。客户提供的文本评论长度不同。通过将文本转换成数字,我们可以用有限长度的向量来表示评论。这样,无论文本长度如何,每次评论的向量长度都是相等的。
单词包是文本到向量的最简单的表示。向量的每一列代表一个单词。行中每个单元格中的值显示一个单词在句子中出现的次数。
例子
作者图片
第一步是找到一个独特单词的词汇表(忽略标点符号和大小写)。上面例子中的词汇:
【这个,电影,是,那个,好,的,时代,不是,我,爱,看,你,会,它,太】
在我们的词汇中,有 15 个独特的单词。所以每一条影评都用一个 15 维的向量来表示(每个词代表一个维度)。对于第一次审查:
作者图片
对应于每个单词的值显示了一个单词在评论中出现的次数。类似地,15 维向量表示剩余的评论。
作者图片
在现实问题中,文本数据在矢量化之前必须进行预处理。预处理包括删除标点符号,将所有单词转换为小写,以及删除对文本没有任何价值的不必要的单词。
在自然语言处理(NLP)中,不必要的单词被称为停用词。nltk 库已经包含了停用词列表。英语中有 179 个停用词。
作者图片
作者图片
我们将看到我们的例子后,删除停用词。
词汇——【电影,好,时代,爱情,看点】
作者图片
请注意,review 1 和 2 的向量是相同的,因为“not”出现在停用词中。该模型认为两个评论是相同的,因为两者的向量是相等的。这个问题的一个解决方案是使用 n 元语法。
n-grams
n 元语法是 n 个单词的相邻序列。n 可以是任何正整数。
例如,“单词包”是三个字母,“文本矢量化”是两个字母。
在我们的例子中,我们使用了一元语法。这意味着每个单词都被认为是一个特征。删除停用词将删除诸如“not”之类可能有用的词。基于单字的单词包(BoW)不考虑序列信息。为了考虑序列信息,我们使用二元语法、三元语法等。
在我们的例子中,如果我们使用二元语法,词汇可以改为:
【这部电影,电影是,是好的,这部电影,不是,不是好的,我爱,爱这个,看电影,看你,你会,会爱,爱它,它太】
因此,n-grams 将比基于单-gram 的模型给出更好的结果。
弓的缺点
- 向量的长度可以很长,大部分值为零。从计算上来说,如果词汇量很大,效率就不高。
- 基于单字的 BoW 无法捕捉文本的上下文。二元模型和三元模型可以完成这项工作,但是计算量很大。
结论
单词包是一种文本矢量化技术,可以将文本转换为有限长度的向量。boW 模型易于实现和理解。单词袋有一些缺点,可以通过使用先进的技术来克服。
感谢阅读!
文本矢量化:词频—逆文档频率(TFIDF)
一种将文本转换成有限长度向量的技术
单词包(BoW)通过计算文档中单词的出现次数将文本转换为特征向量。它没有考虑到文字的重要性。词频—逆文档频率(TFIDF) 基于词袋(BoW)模型,该模型包含关于文档中不太相关和更相关的词的见解。文本中一个词的重要性在信息检索中具有重要意义。
例 —如果你在搜索引擎上搜索某个东西,借助 TFIDF 值,搜索引擎可以给我们与我们的搜索最相关的文档。
我们将详细讨论 TFIDF 如何告诉我们哪个单词更重要?我们将首先分别研究术语频率(TF)和逆文档频率(IDF ),然后在最后将它们结合起来。
术语频率(TF)
它是一个单词(w)在文档(d)中出现频率的度量。TF 被定义为单词在文档中出现的次数与文档中单词总数的比率。公式中的分母项是归一化,因为所有语料库文档的长度都不同。
作者图片
例子
作者图片
第一步是制作一个独特单词的词汇表,并计算每个文档的 TF。TF 对于频繁出现在文档中的单词会更大,而对于文档中不常见的单词会更小。
作者图片
反向文档频率(IDF)
它是衡量一个单词重要性的标准。词频(TF)不考虑词的重要性。有些词如 of、and 等。可能最常见,但意义不大。IDF 基于每个单词在语料库 d 中的频率为其提供权重
单词(w)的 IDF 定义为
作者图片
在我们的例子中,由于语料库中有两个文档,N=2。
作者图片
术语频率—反向文档频率(TFIDF)
它是 TF 和 IDF 的产物。
- TFIDF 给予语料库(所有文档)中不常见的单词更多的权重。
- TFIDF 为在文档中出现频率较高的单词提供了更高的重要性。
作者图片
作者图片
应用 TFIDF 后,A 和 B 文档中的文本可以表示为一个 TFIDF 向量,其维数等于词汇表中的单词。对应于每个单词的值表示该单词在特定文档中的重要性。
为什么我们在 IDF 公式中使用 Ln?
TFIDF 是 TF 与 IDF 的产物。由于 TF 值介于 0 和 1 之间,因此不使用 ln 会导致某些单词的高 IDF,从而支配 TFIDF。我们不希望这样,因此,我们使用 ln ,这样 IDF 就不会完全控制 TFIDF。
TFIDF 的缺点
它无法捕捉到语义。例如,滑稽和幽默是同义词,但是 TFIDF 没有抓住这一点。此外,如果词汇表很大,TFIDF 的计算开销会很大。
结论
词频—逆文档频率(TFIDF)是一种基于词袋(BoW)模型的文本矢量化技术。它比 BoW 模型表现得更好,因为它考虑了单词在文档中的重要性。主要的限制是它不能捕捉单词的语义。TFIDF 的这种局限性可以通过 word2Vec 等更高级的技术来克服。
感谢阅读!
具有分散文本的单口喜剧的文本可视化
使用现代公开可用的工具,文本分析和可视化变得简单而有趣
自然语言处理(NLP)是机器学习的一个分支,处理和分析人类语言,包括语音和文本。数据可视化是指一套用于以视觉格式呈现数据的技术,这种格式更容易被人脑消化并从中发现模式。随着包括文本在内的数据量的增长,用于文本处理、分析和可视化的工具也在发展。
在本文中,我将展示文本分析和可视化可以变得简单而有趣,并且借助现代公开可用的工具,每个人都可以做到这一点,即使没有太多的编程经验。
为了让这篇文章更有趣,我选择了分析起来有趣的开源数据。使用单口喜剧电视特别节目脚本和 NLP 技术来比较不同喜剧演员的想法是受本教程的启发。
本文撰写时(2020 年 3 月),以下单口喜剧电视特别节目在 IMDB 中最受欢迎:
- 大卫·查普尔:棍子和石头
- 汉娜·加斯比:纳内特
- 约翰·木兰尼:新城市
预处理
我从《阁楼的碎片》中搜集了上述节目的剧本,这是一本以电影评论、单口喜剧剧本、采访等为特色的数字杂志。使它们可用于非盈利和教育目的。使用公开可用的工具,我试图首先了解舞台人物,每个喜剧演员最具特征的词语,以及喜剧演员之间词语使用的差异。
在预处理步骤中,我删除了括号中表示被观众打断的文本(例如“[观众笑]”)。
data_df['transcript'] = [re.sub(r'([\(\[]).*?([\)\]])','', str(x)) for x in data_df['transcript']]
然后我使用散点图(Kessler,2017),这是一种围绕散点图构建的公共可用工具,是对最常用词和词云进行排序等传统技术的替代方法。
!pip install scattertext
import scattertext as st
用散文本可视化语料库差异
为了生成我们的第一个可视化,我选择了第一对喜剧演员进行比较,并按照原始教程的步骤进行。我使用 Spacy 将抄本解析成记号。为了构建一个分散文本语料库,我将 category_col 参数设置为“comedian ”,并将解析后的文本传递给 parsed_col 参数。在最后一步中,我将可视化结果写入一个 HTML 文件,以便在浏览器中显示。我指定并命名我希望出现在 y 轴上的类别(即“汉娜·加兹比”),命名我希望与加兹比的抄本进行比较的类别(即“约翰·木兰尼”),并调整其他参数(如 width_in_pixels 和 minimum_term_frequency)。
# chose the comedians to compare
pair1 = 'Hannah Gadsby', 'John Mulaney'
df_pair1 = data_df[data_df['comedian'].isin(pair1)]# parse speech text using spaCy
nlp = spacy.load('en')
df_pair1['parsed'] = df_pair1.transcript.apply(nlp)# convert dataframe into Scattertext corpus
corpus_pair1 = st.CorpusFromParsedDocuments(df_pair1, category_col='comedian', parsed_col='parsed').build()# visualize term associations
html = produce_scattertext_explorer(corpus_pair1,
category='Hannah Gadsby',
category_name='Hannah Gadsby',
not_category_name='John Mulaney',
width_in_pixels=1000,
minimum_term_frequency=5
)
file_name = 'terms_pair1.html'
open(file_name, 'wb').write(html.encode('utf-8'))
IPython.display.HTML(filename=file_name)
通过这几行代码,我得到了一个非常简洁的文本可视化。
图 1
图 1 显示了一个比较汉娜·盖斯比和约翰·木兰尼抄本的散点图的例子。一个点的坐标表示这个词被盖斯比或穆拉尼使用的频率。y 轴上的点越高,Gadsby 使用的就越多,x 轴上的点越靠右,Mulaney 使用的就越多。与喜剧演员之一高度相关的术语出现在图表的左上角或右下角,以及停用词(语言中最常见的词,如“the”、“and”等。)出现在右上角。在加兹比和穆拉尼的记录中不常出现的单词出现在左下角。蓝色的点与加兹比有关,红色的点与穆拉尼有关。通过点击一个点,我得到了关于一个术语在 Gadsby 和 Mulaney 的抄本中的相对使用的统计数据,并且看到了该术语出现的抄本的摘录(图 2)。
图 2
让我们看看我能从中获得的一些快速见解。
穆兰妮用了很多“像”(和“像”)这样的词……我从来没有像“哦,当亲戚问我借钱的时候会是什么样子?加兹比使用“因为”的频率几乎是穆拉尼的三倍(每 25k 项 199 次,而穆拉尼每 25k 项 69 次)……非常聪明,因为这很有趣……因为这是真的。
穆拉尼是一个神经质的人… 只是对很多事情太过焦虑,我一直都很紧张,甚至不是关于生活中的重大事情,只是关于日常情况。当盖斯比学习艺术史时… 你不会在喜剧节目中听到太多关于艺术史的长篇大论,所以…不客气。这些展示了两位喜剧演员在他们的材料中使用的示例主题。
顺便问一下,为什么我没有用查佩尔的文字记录来可视化术语关联?嗯,有太多的“术语”模糊不清…
2016 年大卫·夏佩尔在周六夜现场(GIF 来源:https://gph.is/2ePdMRF)
使用 Empath 可视化主题
在下一个例子中,我使用 Chappelle 的文字记录来可视化移情主题。
Empath (Fast et al .,2016)是一种跨词汇类别(或主题)分析文本的工具,它也可以从文本中生成新的类别(例如,“出血”和“打孔”术语生成类别暴力)。
为了用分散文本可视化 Empath 主题,我安装了 Empath,一个开源 Python 库,并创建了一个提取主题的语料库。我使用源代码为我们的数据调整它。结果如图 3 所示。
!pip install empath# chose the comedians to compare
pair2 = 'Dave Chappelle', 'Hannah Gadsby'
df_pair2 = data_df[data_df['comedian'].isin(pair2)]# parse speech text using spaCy
nlp = spacy.load('en')
df_pair2['parsed'] = df_pair2.transcript.apply(nlp)# create a corpus of extracted topics
feat_builder = st.FeatsFromOnlyEmpath()
empath_corpus_pair2 = st.CorpusFromParsedDocuments(df_pair2,
category_col='comedian',
feats_from_spacy_doc=feat_builder,
parsed_col='parsed').build()# visualize Empath topics
html = produce_scattertext_explorer(empath_corpus_pair2,
category='Dave Chappelle',
category_name='Dave Chappelle',
not_category_name='Hannah Gadsby',
width_in_pixels=1000,
use_non_text_features=True,
use_full_doc=True, topic_model_term_lists=feat_builder.get_top_model_term_lists())
file_name = 'empath_pair2.html'
open(file_name, 'wb').write(html.encode('utf-8'))
IPython.display.HTML(filename=file_name)
图 3 显示了从 Chappelle 和 Gadsby 的材料中提取的移情主题的比较。一个点的坐标表明了这些主题在查佩尔或盖斯比的抄本中出现的频率。Chappelle 最常出现的话题(即 Empath 工具生成的词汇范畴)是‘脏话 _ 术语’,Gadsby 的是‘艺术’。在右上角,我看到了与两位喜剧演员高度相关的话题。其中包括“负面情绪”,以及似乎与之相关的“暴力”、“痛苦”和“仇恨”。“武器”和“犯罪”位于左上角,与 Chappelle 相关,而“愤怒”和“悲伤”为红色,表示与 Gadsby 的关联更强。也就是说,这两个电视特别节目似乎涵盖了各种各样的困难话题…
图 3
如果我为 Chappelle 和 Mulaney 创建一个类似的文本可视化(图 4),我仍然会在右上角看到“负面情绪”,尽管其他类别如“暴力”、“痛苦”、“仇恨”现在都在左侧。这意味着现在这些类别主要只与一个喜剧演员(查佩尔)相关联。如果我看右边的主题或红色的主题,我实际上看不到任何可以直观地归类为“负面情绪”的东西。
图 4
与之前类似,我可以点击类别(例如“负面情绪”)来查看文字记录的摘录,以及每个喜剧演员的类别是由哪些词产生的。
约翰·木兰尼:小子华丽,2018 (GIF 来源:https://tenor.com/3bVP.gif)
这种快速的文本可视化分析有助于你决定今晚在网飞看哪些单口喜剧电视特别节目吗?
结论
本文旨在探索新的公开可用的自然语言可视化和分析工具。我使用基于浏览器的分散文本工具来比较舞台角色,并可视化三个受欢迎的喜剧演员之间的词汇使用差异。选择散射文本是因为它的简单性、漂亮的交互式可视化和很酷的集成功能,如主题提取。原教程提供了更多不同的散点图变体。该工具还会定期更新有趣的特性。下一步是对它们进行测试。
本文首发于 2020 年 3 月 20 日 Dain 工作室网站 。
参考资料:
凯斯勒,J. S. (2017)。Scattertext:一个基于浏览器的工具,用于可视化语料库的不同之处。 arXiv 预印本 arXiv:1703.00565 。
Fast,e .,陈,b .,&伯恩斯坦,M. S. (2016 年 5 月)。移情:理解大规模文本中的主题信号。在2016 年中国计算机学会计算系统中人的因素会议论文集(第 4647–4657 页)。https://arxiv.org/pdf/1602.06979.pdf
Text2emotion:从文本数据中检测情绪的 Python 包
这个软件包将帮助你发现文本信息中的情感
介绍
运动是一种与感觉和想法相一致的精神状态,通常指向一个特定的目标。情绪是一种行为,它反映了我们与他人的互动或与某一事件相关的个人意义或观点。人类能够从文本数据中识别情感,并且能够理解文本的内容。但是如果你想想机器,它们能从文本中识别情感吗?
从本文中,您将了解如何使用这个 python 包并从文本数据中提取情感。
目录
- 什么是 text2emotion?
- 怎么用?
- 行业用例详情
- 结论
- 关键要点
不要担心,我会保证你不会厌倦阅读这篇文章,并且会尝试用最简单的方式来解释它。
我们开始吧,
1。什么是 text2emotion?
Text2emotion 是为了寻找文本数据中嵌入的合适情感而开发的具有明确内涵的 python 包。该研究称,当人类在思考过程中,他对自己的陈述非常确定时,他会以正确的方式表达自己的情绪,并且在表达这些情绪的词语的情况下,会进行适当的调整。
如果我想用简单的话告诉你,然后考虑如果客户不喜欢投入大量资金的产品,那么他肯定会给出类似 的反馈:“我对你的产品服务非常生气,我要就此问题提出投诉”。 当你读到这种反馈时,你会确信客户对产品服务非常不满,我们必须尽快改进。 Text2emotion 作品,以同样的方式,从文本中提取情感。
让我们总结一下, Text2Emotion 是 python 包,它将协助您从内容中提取情感。
- 处理任何文本数据,识别其中嵌入的情感,并以字典的形式提供输出。
- 很好地适应了 5 个基本情绪类别,如快乐、愤怒、悲伤、惊喜、和恐惧。
2.怎么用?
安装 text2emotion 包的最好方法是使用 pip。
#Install package using pip
**pip install text2emotion**
让我们导入构建模型所需的必要库。
#Import the modules
**import text2emotion as te**
获取一些内容来评估性能。
**text** = "I was asked to sign a third party contract a week out from stay. If it wasn't an 8 person group that took a lot of wrangling I would have cancelled the booking straight away. Bathrooms - there are no stand alone bathrooms. Please consider this - you have to clear out the main bedroom to use that bathroom. Other option is you walk through a different bedroom to get to its en-suite. Signs all over the apartment - there are signs everywhere - some helpful - some telling you rules. Perhaps some people like this but It negatively affected our enjoyment of the accommodation. Stairs - lots of them - some had slightly bending wood which caused a minor injury."
现在我们必须使用上面定义的文本参数调用 get_emotion()函数。
#Call to the function
**te.get_emotion(text)**#The output we received,
**{'Angry': 0.12, 'Fear': 0.42, 'Happy': 0.04, 'Sad': 0.33, 'Surprise': 0.08}**
这里,我们得到了字典方面的输出,其中有情感类别以及相应的分数。
现在,如果我们考虑一下相对情绪类别的分数,那么恐惧分数是0.42T44悲伤分数是 0.33 。所以从整体上分析,我们可以说我们作为输入的陈述带有恐惧&悲伤的语气。
这个包的好处是,它能够从描述人类行为的表情符号中识别情感。让我们举个例子,
**text = "Day was pretty amazing😃😃"
te.get_emotion(text)**#Output
**{'Angry': 0.0, 'Fear': 0.0, 'Happy': 0.8, 'Sad': 0.0, 'Surprise': 0.2}**
从输出可以得出,文字输入属于开心和惊喜的情感范畴。
我们为你设计了一个网络应用程序,你可以在那里试验所有这些东西。我提供了下面的链接,请随意查看。
- 在输入框中输入文本。
Web 应用程序演示
2.点击提交按钮并获得输出。
Web 应用程序输出
查看 web app 这里 。
3.行业用例细节
作为一个组织,我们总是关注客户的意见、反馈或改进。顾客对我们的产品有什么看法?他可能高兴或悲伤或生气,也可能是因为你永远不知道的组织所提供的服务。这就是 text2emotion 可以成为你的救世主的条件。在这里,我将讨论两个真实的行业用例。
电子商务行业:客户参与终点
分析通过各种来源从客户处收到的输入,例如来自聊天机器人的文本数据、来自联络中心的日志、电子邮件、等。跟踪这些音调信号可以帮助客户服务经理改善他们的团队与客户的互动方式。
社交媒体监控
在当今的数字世界中,品牌监控和声誉管理已经成为每个业务部门最重要的方面之一。这就是情感分析发挥作用的地方。这将有助于公司:跟踪消费者对公司的看法,通过提供具体细节指出消费者的态度,发现不同的模式和趋势,密切关注影响者的演示。
4.结论
我希望您已经了解了这个库提供的基本功能,以及它如何对不同的行业用例有用。如果你喜欢这个包,并想设计一些有趣的实现和创新的行业用例,那么我们将很高兴展示它。
5.关键要点
感谢您阅读文章。
你好👋我希望你能从我的文章中获得知识。如果你愿意支持我,请随意给我买些咖啡🙏☕
https://www.buymeacoffee.com/amey23
或者联系我,ameypband23@gmail.com
很乐意帮助你。
TextBlob 拼写更正
本文解释了如何使用 TextBlob 模块进行拼写纠正。
什么是 TextBlob?
T extBlob 是一个用于处理文本数据的 Python 库。它提供了一个一致的 API,用于处理常见的自然语言处理(NLP)任务,如词性标注、名词短语提取、情感分析等。
为什么是 TextBlob?
NLU 是 NLP 的一个子集,其中非结构化数据或句子被转换成结构化形式,以在处理端到端交互方面执行 NLP。关系提取、语义解析、情感分析、名词短语提取是 NLU 的几个例子,它本身是 NLP 的子集。现在,在这些领域中,TextBlob 扮演了一个重要的角色,而 NLTK 并没有有效地做到这一点。
使用 TextBlob 进行拼写更正
由 Romain Vignes 在 Unsplash 上拍摄的照片
步骤:1 →安装 TextBlob
有时推文、评论或任何博客数据可能包含打字错误,因此首先我们需要纠正这些数据,以减少代表相同意思的相同单词的多个副本。
在你的电脑上安装 TextBlob 非常简单。你只需要使用 pip 安装它。
步骤 2 →加载预处理输入
我们必须从基础开始给计算机喂食,这样它才能在自然语言理解和处理方面得到很好的训练。
接下来,加载您需要正确拼写的输入文本(如 docx ),我们将要处理的文本是多伦多的移民。
这里,我们需要使用 regex 清理输入文本,因为我们不需要任何数字字符。
这里,清理后的文本输入
People have travelled through and inhabited the Toronto area, located on a broad sloping plateau interspersed with rivers, deep ravines, and urban forest, for more than , years. After the broadly disputed Torronto Purchase, when the Mississauga surrendered the area to the British Crown, the British established the town of York in and later designeted it as the capital of Upper Canada. During the War of , the town was the site of the Battle of York and suffered heavy damage by American troops. York was renamed and incorporated in as the city of Toronto. It was designated as the capitel of the province of Ontario in during Canadian Confederation. The city proper has since expanded past its original borders through both annexation and amalgamation to its current area of . km . sq mi . The diverse population of Tornto reflects its current and historical role as an important destination for immigrants to Canada. More than percent of residants belong to a visible minority population group, and over distinct ethnic origins are represented among its inhabitats. While the majority of Torontonians speak English as their premary language, over languages are spoken in the city. Toront is a prominent center for music, theatre, motion picture production, and tilevision production, and is home to the headquarters of Canada s major notional broadcast networks and media outlets. Its varied caltural institutions, which include numerous museums and gelleries, festivals and public events, entertaiment districts, national historic sites, and sports actevities, attract over million touriets each year. Torunto is known for its many skysvrapers and high rise buildinds, in particalar the tallest free standind structure in the Western Hemisphere, the CN Tower.
步骤 3 →识别拼错的单词
首先,识别拼写错误的标记,以纠正它们的拼写。为此,我们可以使用拼写检查器模块中的拼写检查器功能。
输出:
{'skysvrapers', 'entertaiment', 'standind', 'residants', 'rivers,', 'forest,', 'torronto', 'torunto', 'actevities,', 'touriets', 'toront', 'particalar', 'gelleries,', 'production,', 'designeted', 'purchase,', 'hemisphere,', 'area,', 'capitel', 'ravines,', 'premary', 'inhabitats.', 'ecents,', 'tilevision', 'toronto.', 'tornto', 'buildinds,', 'confederation.', 'sites,', 'caltural', 'theatre,', 'institutons,', 'language,', 'music,', 'troups.', 'torontonians', 'mississauga'}
步骤 4 →纠错过程
用 TextBlob 对象存储更新后的文本。
使用[**correct()**](https://textblob.readthedocs.io/en/dev/api_reference.html#textblob.blob.TextBlob.correct)
方法尝试拼写纠正。
输出:
People have travelled through and inhabited the Toronto area, located on a broad sloping plateau interspersed with rivers, deep ravines, and urban forest, for more than , years. After the broadly disputed Torronto Purchase, when the Mississauga surrendered the area to the British Grown, the British established the town of Work in and later designed it as the capital of Upper Canada. During the War of , the town was the site of the Battle of Work and suffered heavy damage by American troops. Work was renamed and incorporated in as the city of Toronto. It was designate as the capital of the province of Ontario in during Canadian Confederation. The city proper has since expanded past its original borders through both annexation and amalgamation to its current area of . km . sq mi . The diverse population of Onto reflect its current and historical role as an important destination for immigrants to Canada. More than percent of residents belong to a visible minority population group, and over distinct ethnic origins are represented among its inhabitants. While the majority of Torontonians speak English as their primary language, over languages are spoken in the city. Front is a prominent center for music, theatre, motion picture production, and television production, and is home to the headquarters of Canada s major national broadcast network and media outlets. Its varied cultural institutions, which include numerous museums and galleries, festival and public events, entertainment districts, national historic sites, and sports activities, attract over million tories each year. Torunto is known for its many skysvrapers and high rise buildings, in particular the tables free standing structure in the Western Hemisphere, the of Power.
完整代码可在 GitHub 获得
结论
我希望您现在已经理解了如何使用 TextBlob 模块进行拼写纠正。感谢阅读!
Textfeatures:用于从文本数据中提取基本特征的库
弗兰基·查马基在 Unsplash 上拍摄的照片
简介:文本特征
当我们处理文本数据时,我们总是关心数据特征、数据的预处理以及更可能的预测。为了改进我们的模型,理解数据并在数据中找到更有趣的特征是很重要的,比如标签、链接等等。
什么是文本特征?
这是一个 python 包,可以帮助你从文本数据中提取基本特征,如标签、停用词、数字,这将帮助你理解数据,更有效地改进你的模型。
函数调用结构:
function_name(dataframe,“text_column”,“new _ column”)
在哪里,
数据帧:- 数据帧的名称
text_column:- 要从中提取特征的列的名称。
new_column:- 通过从 text_column 提取特征而导出的新列。
textfeatures 将为您提供什么服务?
1。word_count():- 给出文本数据中的总字数。
2。char_count():- 给出字符数。
3。avg_word_length():- 给出平均单词长度。
4。 :-给出停用词的计数。
5。停用字词() :-从文本数据中提取停用字词。
6。hashtags_count():- 给出 hashtags 的计数。
7。hashtags():- 从文本数据中提取 hashtags。
8。links_count():- 给出文本数据中嵌入的链接数。
9。links():- 从文本数据中提取链接。
10。numeric_count():- 给出数字的位数。
11。user_mentions_count():- 从文本数据中给出用户提及次数。
12。user_mentions():- 从文本数据中提取用户提及。
13。clean():- 给出去除文本数据中不必要材料后的预处理数据。
让我们了解一下 textfeatures 包提供的语法和功能。
我们使用的是 Kaggle 上的新冠肺炎推特数据集。
安装 textfeatures 包的最佳方式是使用 pip。
pip install textfeatures
让我们导入构建模型所需的必要库。
import textfeatures as tf
import pandas as pd
使用 pandas 读取数据 CSV 文件,并用数据框定义它。预览数据集。
#enconding is applicable for this dataset.
df = pd.read_csv("COVID-19_Tweets.csv",encoding="latin")
df.head()
图 1:数据集预览
1.字数()
- 这是特征提取的首要任务。
- 我们计算数据集中每一行的字数。
tf.word_count(df,"Tweets","word_cnt")
df[["Tweets","word_cnt"]].head()
图 2:字数
2.char_count()
- 我们计算数据集中每一行的字符数。
- 这可以通过计算推文的长度来实现。
tf.char_count(df,"Tweets","char_len")
df[["Tweets","char_len"]].head()
图 3:字符长度
3.平均单词长度()
- 为了更好地理解这些数据,我们将找出平均单词长度。
- 我们简单地计算出所有单词的长度之和,然后除以推文的总长度。
tf.avg_word_length(df,"Tweets","avg_wrd_length")
df[["Tweets","avg_wrd_length"]].head()
图 4:平均单词长度
4.停用字词计数()
- 为了处理任何自然语言处理问题,我们总是试图清理我们的数据。所以找到停用词是首要任务。
- 我们将找到文本数据中出现的停用词的计数。
tf.stopwords_count(df,"Tweets","stopwords_cnt")
df[["Tweets","stopwords_cnt"]].head()
图 5:停用词计数
5.停用词()
- 我们在文本数据中找到停用词,并将其存储在一个列表中,以便您可以找到数据中的干扰,并使其更具交互性。
tf.stopwords(df,"Tweets","stopwords")
df[["Tweets","stopwords"]].head()
图 6:停用词
6.hashtags_count()
- 找出数据中的标签是最有趣的任务,因为标签帮助我们获得最大的观众,这样你的帖子或推文也会得到最大的响应。
- 我们将首先计算标签的数量。
tf.hashtags_count(df,"Tweets","hashtags_count")
df[["Tweets","hashtags_count"]].head()
图 7:标签数
7.标签()
- 现在,我们将提取标签并将其存储到列表中,以便对数据进行更多的预处理和可视化。
tf.hashtags(df,"Tweets","hashtags")
df[["Tweets","hashtags"]].head()
图 8:标签
8.links_count()
- 为了从数据中找到更多的见解,我们还找到了嵌入的链接。
- 我们将找到链接的计数。
tf.links_count(df,"Tweets","links_count")
df[["Tweets","links_count"]].head()
图 9:链接计数
9.链接()
- 让我们找出嵌入在文本数据中的链接,将其存储在列表中并用于进一步的分析。
tf.links(df,"Tweets","Links")
df[["Tweets","Links"]].head()
图 10:链接
10.数字计数()
- 就像我们搜索单词、标签、链接和许多其他东西一样,我们也会发现数字的数量。
- 对我们处理文本数据肯定会有帮助。
tf.numerics_count(df,"Tweets","num_len")
df[["Tweets","num_len"]].head()
图 11:数字计数
11.用户提及次数计数()
- 在处理 twitter 数据时,我们总是会接触到用户提及(@)。我们对这种类型的数据特征很好奇,它帮助我们更有效地分析数据并理解它的重要性。
- 我们在这里找到了用户提及的次数。
tf.user_mentions_count(df,"Tweets","user_mentions_cnt")
df[["Tweets","user_mentions_cnt"]].head()
图 12:用户提及计数
12.用户提及次数()
- 让我们找出用户提及,将其存储在列表中并用于信息可视化。
tf.user_mentions(df,"Tweets","user_mentions")
df[["Tweets","user_mentions"]].head()
图 13:用户提及
13.清洁()
- 在提取了所有有意义的特征之后,我们需要清理数据以进行进一步的情感分析。
- 因此,我们有一个 clean()函数,它将在删除不需要的数据(如数字、停用词、标点符号和链接)后,为您提供预处理的数据。
tf.clean(df,"Tweets","Clean_tweets")
df[["Tweets","Clean_tweets"]].head()
图 14:清洁()
结论
我希望你理解这个库提供的基本功能。现在是时候让你设计一些有趣的实现了。如果你想做贡献,那么请把资源库放到 GitHub 上,继续做好工作。
关键要点
享受学习!
你好👋我希望你能从我的文章中获得知识。如果你愿意支持我,请随意给我买些咖啡🙏☕
https://www.buymeacoffee.com/amey23
或通过 ameypband23@gmail.com 联系我
乐意为您效劳。
tf.data:创建数据输入管道
你不能把你的数字数据载入内存吗?您的模型是否必须在每个时期后等待数据加载?
你的 Keras DataGenerator 慢吗?
tensor flowTF . dataAPI 允许构建复杂的输入管道。它可以轻松处理大量数据,可以读取不同格式的数据,同时允许复杂的数据转换。
我们为什么需要 tf.data?
一个训练步骤包括以下步骤:
1。文件读取
2。获取或解析数据
3。数据转换
4。使用数据来训练模型。
来源:张量流 (CC0)
如果你有大量的数据,而你无法将它加载到内存中,你可能想要使用生成器。但是生成器的可移植性和可伸缩性有限。
在每个时期之后,您将等待模型将数据转换成可消费的格式,在此期间,您的模型处于闲置状态,不进行任何训练。这导致 CPU 和 GPU 利用率低。
处理这个问题的一个解决方案是 预先预取 您的数据,这样您就不必等待数据被加载。
来源:张量流 (CC0)
tf.data 是一个数据输入管道构建 API,您可以使用它来轻松构建您的数据管道。无论您是想从本地文件中读取数据,还是远程存储数据。
加载分类数据
为了训练图像分类模型,我们创建了一个 CNN 模型,并将我们的数据提供给该模型。我想训练一个猫和狗的分类器,我的数据存储在下面的文件夹结构中。
*data
└── train
├── cat -> contains images of cats
└── dog -> contains images of dogs*
我们首先找到所有图像的路径——
from glob import glob
import tensorflow as tfimage_path_list = glob('data/train/*/*.jpg')
data = tf.data.Dataset.list_files(image_path_list)
TF . data . Dataset . list _ files将 glob 方法返回的列表转换为 Dataset 对象。现在,我们将加载图像和它们的类。
def load_images(path):
image = tf.io.read_file(path)
image = tf.io.decode_image(image)
label = tf.strings.split(path, os.path.sep)[-2] return image, labeldata = data.map(load_images)
于是, 数据 对象现在有了图像和标签。但这不是它,我们将不得不调整图像的大小,预处理和应用转换。
def preprocess(image, label):
image = tf.image.resize(image, (IMG_HEIGHT, IMG_WIDTH))
image = tf.image.random_flip_left_right(image)
image = tf.image.random_flip_up_down(image) image /= 255.
image -= 0.5 return image, labeldata = data.map(preprocess)
我基于 tf.data 创建了一个名为Chitra的小库,可以用来跳过所有这些步骤。
*from chitra import dataloader as dl
path = './data/train'train_dl = dl.Clf()
data = train_dl.from_folder(path, target_shape=(224, 244), shuffle = True)# to visualize the data
train_dl.show_batch(6, figsize=(6,6))*
您只需指定数据的路径,它就会以目标大小加载。
**你可以在https://github.com/aniketmaurya/chitra找到我的代码
PySpark 中使用 Map-Reduce 算法计算 TF-IDF
在本文中,我们将使用 Pyspark 中的 Map-Reduce 算法来计算每个文档中各个单词的词频-逆文档频率(TF-IDF)分数。
简介
不过,Spark MLlib 有一个计算 TD-IDF 分数的内置函数,它利用 map/reduce 算法以分布式方式运行代码。在本文中,我们将使用弹性分布式数据集(rdd)来实现 map/reduce 算法,以便更好地理解底层概念。下图解释了单词计数示例的 map/ reduce 算法的基本思想。最初,我们将文本数据的每一行中出现的单词进行拆分,并在 map 步骤中生成一个新的键/值对列表。此外,在 reduce 步骤中,我们为每个不同的关键字(单词)获取一个值列表,并为每个不同的关键字生成新的聚合值。
字数的 Map-Reduce 示例
在本文中,我们不会深入探讨 map/reduce 算法的理论。对于这方面的新手,我建议他们首先阅读下面给出的文章,以便掌握 Spark 和 Hadoop 中 map/reduce 的概念。
MapReduce 是一个集群数据处理框架。由 Map 和 Reduce 函数组成,它分发数据…
medium.com](https://medium.com/@elmaslouhy.mouaad/understanding-hadoop-mapreduce-f3e206cc3598) [## MapReduce 解释道
介绍和简要说明
medium.com](https://medium.com/@francescomandru/mapreduce-explained-45a858c5ac1d)
TF-IDF 配方
TF-IDF 是一种为任何文本数据提取特征的方法。它使用术语频率和逆文档频率进行计算。
其中 N 表示单词在文档中的出现频率。IDF 分数告诉我们一个特定的单词在整个语料库中有多重要。例如,如果一个单词出现在每个文档中,那么 IDF 分数将为零。
计算词频(TF)
词频是特定单词在文档中出现的次数。首先,让我们创建一个玩具数据集如下-
我们的数据集由三行/文档组成,分别与文档 id 相关联。我们可以把它想象成一个键/值对,其中键是文档 id,值是与各自的文档 id 相关联的文本。
步骤 1:将键/值对映射到新的键/值对。
在这一步中,我们将现有的键/值对映射到一个新的键-值对,这个新的键-值对由作为键的 document-id 和 token 以及作为值的 1(表示计数)组成。我们将在这里使用 flatMap 转换将所有的标记合并到一个列表中。
步骤 2:减少键/值对
在这一步中,我们将使用公共键对键/值对进行分组,并进一步聚合同一个键的值,以获得与其文档 id 相对应的特定单词的词频。
我们可以看到单词“my”在第三个文档中出现了两次,因此它被分组,并且值被求和为 2 (1+1)。
第三步:再次映射
在这一步中,我们将把键/值对更改为一组新的键/值/对,用令牌作为键,用它的文档 id 和各自的术语频率作为值。
计算反向文档频率(IDF)
IDF 分数告诉我们一个特定的单词在整个语料库中有多重要。例如,如果一个单词出现在每个文档中,那么 IDF 分数将为零。
我们知道我们案例的文档总数是 3,因为我们在完整的语料库中有 3 行。因此,我们的下一步将是计算有多少文档包含特定的标记 w。此外,我们可以使用此信息来计算语料库中所有标记的 IDF 分数。
在本节中,我们将链接前面的 map/reduce 计算来计算 IDF。在上一节的简化步骤之后,我们获得了各个令牌的键/值对及其词频。
第一步:映射
在这一步中,我们将把以前的键/值对映射到一个新的键/值对。这里,关键字将是令牌,其值将是该令牌的文档 id TF 以及计数器 1。这里的这个表示文档 id 中存在一个与之相关联的单词。
第二步:映射
在这一步中,我们将提取令牌和表示它在某些文档中出现的计数器 1 的数量。
步骤 3:通过按键减少
在这一步中,我们将按键进行缩减,以获得包含特定标记 w 的文档数。例如,两个文档中发生了编织,因此在分组和聚合后,其总和将为 2 (1+1)。
现在,由于我们有了包含每个标记 w 的文档数,我们将使用对数变换来映射这个最终输出,以计算 IDF 分数。
输出
计算 TD-IDF:
现在我们有两个 rdd,一个有文档 id,另一个有每个令牌的文档术语频率。另一个 RDD,每个令牌都有 IDF 分数。
我们将执行一个内部连接,为每个令牌分配一个文档 id、TF 和 IDF 分数。
现在,我们将映射这个函数,将与各个文档 id 相关联的每个令牌的 TF 和 IDF 值相乘。
让我们将最终输出转换为 Pyspark 数据框,以便更清楚地显示分数,并将其与实际理论计算的 TF-IDF 分数进行比较。
预测
实际分数[1]
从上面的数据框架和表格中我们可以清楚地看到,所有的 TF-IDF 分数都与理论分数完全一致。
完整代码
上述实现的完整代码如下所示。这可能看起来很可怕,但没有什么特别的。它仅由链接在一起以获得最终输出的多个 map/ reduce 步骤组成。
参考文献
[1]访问:data camp . com/community/tutorials/text-analytics-初学者-nltk
感谢您的阅读!!!!
如果你喜欢我的工作并想支持我:
支持我的最好方式就是跟随我上 中级 。
2-在LinkedIn上关注我。
TF-IDF 讲解和 Python Sklearn 实现
什么是 TF-IDF,如何用 Python 和 Scikit 实现它-Learn。
TF-IDF 是一个信息检索和信息抽取子任务,旨在表达一个词对一个文档的重要性,该文档是我们通常称为语料库的文档集合的一部分。它通常被一些搜索引擎用来帮助他们获得与特定查询更相关的更好的结果。在本文中,我们将讨论什么是 TF-IDF,解释它背后的数学原理,然后我们将看到如何使用 Scikit-Learn 库在 Python 中实现它。
本文原载于 程序员背包博客 。如果你想阅读更多这类的故事,一定要访问这个博客。
非常感谢您阅读本文!对更多这样的故事感兴趣?在 Twitter 上关注我,地址是@ b _ dmarius,我会在那里发布每一篇新文章。
文章概述
- 什么是 TF-IDF
- TF-IDF 公式解释
- TF-IDF sklearn python 实现
- TfIdfVectorizer 与 TfIdfTransformer —有何不同
- TF-IDF 应用
- 结论
什么是 TF-IDF
TF-IDF 代表词频——逆文档频率,是一种统计数据,旨在更好地定义一个单词对一个文档的重要性,同时也考虑到与同一语料库中其他文档的关系。
这是通过查看一个单词在一个文档中出现了多少次,同时也注意相同的单词在语料库中的其他文档中出现了多少次来执行的。
这背后的基本原理如下:
- 在文档中频繁出现的单词与该文档的相关性更高,这意味着该文档与该特定单词相关的概率更高
- 在更多文档中频繁出现的单词可能会阻止我们在集合中找到正确的文档;这个词要么适用于所有文档,要么不适用。无论哪种方式,它都不能帮助我们从整个集合中过滤出单个文档或文档的一个子集。
那么 TF-IDF 是一个分数,它应用于我们数据集中每个文档中的每个单词。并且对于每个单词,TF-IDF 值随着该单词在文档中的每次出现而增加,但是随着在其他文档中的每次出现而逐渐减少。数学会在下一节讲到。
TF-IDF 公式解释
现在让我们看看 TF-IDF 统计指标背后的简单公式。首先让我们定义一些符号:
- N 是我们数据集中的文档数量
- d 是我们数据集中的一个给定文档
- D 是所有文件的集合
- w 是文档中的一个给定单词
第一步是计算词频,我们的第一个衡量指标是得分。
词频公式
这里 f(w,d) 是 w 在 d. 文档中的出现频率
第二步是计算逆项频率。
逆文档频率公式
对于数据集中的 N 个文档和整个数据集中的 f(w,D) 单词 w 的出现频率,这个数字会随着单词在整个数据集中的出现次数的增加而降低。
最后一步是通过以下公式计算 TF-IDF 得分:
词频—逆文档频率—公式
TF-IDF Sklearn Python 实现
有了像 scikit-learn 这样令人敬畏的库,实施 TD-IDF 就轻而易举了。首先,我们需要为我们的项目安装 2 个依赖项,所以现在就开始吧。
pip3 install scikit-learn
pip3 install pandas
为了看到 TF-IDF 的全部威力,我们实际上需要一个适当的、更大的数据集。但是出于本文的目的,我们只想关注实现,所以让我们将依赖项导入到我们的项目中,并构建我们的迷你数据集。
import pandas as pd
from sklearn.feature_extraction.text import TfidfTransformerdataset = [
"I enjoy reading about Machine Learning and Machine Learning is my PhD subject",
"I would enjoy a walk in the park",
"I was reading in the library"
]
现在让我们计算 TF-IDF 分数并打印出结果。
tfIdfVectorizer=TfidfVectorizer(use_idf=True)
tfIdf = tfIdfVectorizer.fit_transform(dataset)
df = pd.DataFrame(tfIdf[0].T.todense(), index=tfIdfVectorizer.get_feature_names(), columns=["TF-IDF"])
df = df.sort_values('TF-IDF', ascending=False)
print (df.head(25))
现在让我们看看我们的结果。
TF-IDF
machine 0.513720
learning 0.513720
about 0.256860
subject 0.256860
phd 0.256860
and 0.256860
my 0.256860
is 0.256860
reading 0.195349
enjoy 0.195349
library 0.000000
park 0.000000
in 0.000000
the 0.000000
walk 0.000000
was 0.000000
would 0.000000
TfidfVectorizer 与 TfidfTransformer —有何不同
如果您曾经看过 TF-IDF 的其他实现,您可能已经看到使用 Scikit-Learn 实现 TF-IDF 有两种不同的方式。一种是使用 TfidfVectorizer 类(就像我们刚才做的那样),另一种是使用 TfidfTransformer 类。你可能想知道它们之间有什么不同,所以让我们来讨论一下。
从理论上讲,这两种实现实际上没有区别。实际上,如果我们想使用 TfidfTransformer,我们需要编写更多的代码。这两种实现的主要区别在于,TfidfVectorizer 为您执行词频和逆文档频率,而使用 TfidfTransformer 需要您使用 Scikit-Learn 中的 CountVectorizer 类来执行词频。
因此,让我们来看一个替代的 TF-IDF 实现,并验证结果是否相同。我们首先需要导入 2 个额外的依赖项到我们的项目中。
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer
我们将使用与其他实现相同的小型数据集。让我们编写替代实现并打印出结果。
tfIdfTransformer = TfidfTransformer(use_idf=True)
countVectorizer = CountVectorizer()
wordCount = countVectorizer.fit_transform(dataset)
newTfIdf = tfIdfTransformer.fit_transform(wordCount)
df = pd.DataFrame(newTfIdf[0].T.todense(), index=countVectorizer.get_feature_names(), columns=["TF-IDF"])
df = df.sort_values('TF-IDF', ascending=False)
print (df.head(25))
你可以看看结果,看到和上面的一样。
TF-IDF
machine 0.513720
learning 0.513720
about 0.256860
subject 0.256860
phd 0.256860
and 0.256860
my 0.256860
is 0.256860
reading 0.195349
enjoy 0.195349
library 0.000000
park 0.000000
in 0.000000
the 0.000000
walk 0.000000
was 0.000000
would 0.000000
TF-IDF 应用
我们已经看到,使用正确的工具实现 TF-IDF 非常容易,但该算法的应用程序非常强大。TF-IDF 最常见的两种使用情形是:
- 信息检索:通过计算用户查询相对于整个文档集的 TF-IDF 得分,我们可以计算出一个文档与给定查询的相关程度。有传言说,大多数搜索引擎都使用某种 TF-IDF 实现,但我自己无法证实这一信息,所以对此要持保留态度。
- 关键词提取 :根据 TF-IDF 得分,文档中排名最高的词可以很好地代表该文档的关键词(因为它们使该文档从其他文档中脱颖而出)。因此,我们可以很容易地使用某种 TF-IDF 分数计算来从文本中提取关键字。
结论
因此,在本文中,我们看到了对什么是 TF-IDF 以及我们如何从数学上解释它的解释。然后,我们看到了使用 Scikit-Learn Python 库的两个替代实现。然后我们讨论了这个算法的一些可能的应用。我希望你喜欢这个!
非常感谢您阅读这篇文章!有兴趣了解更多吗?在 Twitter 上关注我,地址是@ b _ dmarius,我会在那里发布每一篇新文章。
TF-IDF 概述
来自 pixabay.com 的照片
通过 5 个简单的例子了解 TF-IDF 的发展
彼得·鲁恩,照片来自 alchetron.com
早在 1958 年,汉斯·彼得·鲁恩在他的论文《文献摘要的自动生成》中假设“一篇文章中单词出现的频率提供了一个有用的单词重要性的度量”,这可能是迄今为止信息检索科学中最重要的事情之一,并被用于所有知名的大大小小的搜索引擎,从谷歌和雅虎到定制搜索解决方案,如弹性搜索和蝎狮搜索。
Luhn 假设的重要性不能被高估,并且在信息检索领域的许多进一步的科学研究都是基于 Luhn 的假设,尽管并非所有的研究都提到,随着时间的推移,它可能成为公理。
在这篇文章中,我将尝试用非常清晰的例子来展示 TF 代表“词频”和它的对应词 IDF 代表“逆文档频率”是如何帮助找到你要找的东西的。
让我们看 5 份文件:
1\. My dog doesn't like all my pets, but me and my wife do love them all. A dog cannot be a cat lover
2\. Cats and dogs do not like each other, but my cat does like dogs
3\. Walking a dog is a good start of the day
4\. Not all cats like walking, but some cats do
5\. All dogs like walking, but mine doesn't like. It's like so weird
并尝试为不同的查询对它们进行排序(即找到最相关和最不相关的以及顺序)。
如果单词出现,权重= 1,否则为 0
查询:‘喜欢’。
注意,我们不知道查询中的“like”是什么意思:比如“A 就像 B”或“我喜欢 smth”。那么我们能做些什么来排列文档呢?
首先想到的是,如果一个文档根本不包含单词“like ”,那么它可能不太相关。因此,顺序可以是:
1\. My dog doesn't **like** all my pets, but me and my wife do love them all. A dog cannot be a cat lover
2\. Cats and dogs do not **like** each other, but my cat does **like** dogs
4\. Not all cats **like** walking, but some cats do
5\. All dogs **like** walking, but mine doesn't **like**. It's **like** so weird
3\. Walking a dog is a good start of the day
我们把文档#3 放在最后,因为它不包含“like”。至于其余的,我们没有改变他们的顺序,因为他们都包含“喜欢”。这样的排名质量够好吗?我不这么认为,因为在前 4 个文档中,有 2 个(#5 和#2)似乎比其他文档更相关,因为它们提供了更多与查询术语“like”相关的事实(请记住,我们不知道“like”在查询中的确切含义),但它们不是前 2 个。那么我们能做些什么呢?
权重=术语频率
查询:‘喜欢’。
如前所述,根据 Luhn 的假设,包含查询术语的更多出现的文档可能更相关。让我们根据文档的词频对其进行排序(注意,在这里以及在信息检索领域,一般来说“频率”只是指计数,而不是像在物理学中那样用计数除以某个值):
5\. All dogs **like** walking, but mine doesn't **like**. It's **like** so weird | tf = 3
2\. Cats and dogs do not **like** each other, but my cat does **like** dogs | tf = 2
1\. My dog doesn't **like** all my pets, but me and my wife do love them all. A dog cannot be a cat lover | tf = 1
4\. Not all cats **like** walking, but some cats do | tf = 1
3\. Walking a dog is a good start of the day | tf = 0
这解决了问题,现在文档#5 和#2 在最上面。
但是现在让我们尝试另一个查询——“我的一天”:
1\. **My** dog doesn't like all **my** pets, but me and **my** wife do love them all. A dog cannot be a cat lover | tf = 3
2\. Cats and dogs do not like each other, but **my** cat does like dogs | tf = 1
3\. Walking a dog is a good start of the **day** | tf = 1
4\. Not all cats like walking, but some cats do | tf = 0
5\. All dogs like walking, but mine doesn't like. It's like so weird | tf = 0
文档#1 和#2 得到最高的位置,但是它是正确的吗?他们没有提到“天”,只提到了“我的”:我的宠物、我的妻子、我的猫,考虑到这个问题,这些可能也很重要,但很可能不是。文档#3 的位置较低,但它似乎是最相关的。怎么才能放高点?
让我们想想“我的”和“日”以及其他词有什么不同。“day”似乎更具体,而“my”几乎可以适用于任何事物,在文档中,“my”出现的频率远远高于“day”和其他更具体的术语。接下来是“文档频率”——公式的另一个重要部分,也是术语权重的第二个基石。
1972 年,英国计算机科学家 Karen sprck Jones 在她的一篇学术论文中说:
凯伦·斯帕克·琼斯,照片来自 en.wikipedia.org
文档描述的详尽性是它包含的术语的数量,术语的具体性是它所属的文档的数量
我们已经讨论了前一部分,现在让我们看看如果我们考虑后一部分,文档顺序是如何变化的。
权重=术语频率*(文档数量/文档频率)
查询:‘我的一天’。
“My”出现在两个文档中,“day”只出现在一个文档中,因此“my”的 IDF(逆文档频率)为 5(文档总数)/ 2 = 2,5,“day”-5/1 = 5。
1\. **My** dog doesn't like all **my** pets, but me and **my** wife do love them all. A dog cannot be a cat lover
Weight = 3(tf of 'my') * 2,5(idf of 'my') + 0(tf of 'day') * 5 (its idf) = 7,5
3\. Walking a dog is a good start of the **day**
Weight = 0 * 2,5 + 1 * 5 = 5
2\. Cats and dogs do not like each other, but **my** cat does like dogs
Weight = 2,5
4\. Not all cats like walking, but some cats do
Weight = 0
5\. All dogs like walking, but mine doesn't like. It's like so weird
Weight = 0
在 IDF 的帮助下,我们可以将相关文档“遛狗是一天的良好开端”放在更高的位置,但它仍然不在最上面。原因是什么?这是因为“我的”出现得太频繁了,超过了“当天的 IDF”的重要性。我们如何解决这个问题?我们需要降低 TF 贡献的东西。多年来,从对数(TF)到所谓的“术语频率饱和曲线”,已经提出并研究了许多 TF 归一化的变体。为了简单起见,我们只取对数变量。
重量= log(1+TF) * (N/df)
与线性函数相比,对数平滑的整体思想是自变量越高,它增加得越慢,即,对于较小的 x,它增长得越快,而对于较大的 x,它增长得越慢。要求函数中的“1+x”在项频率= 0 的自变量中不具有 0,因为 log(0)是未定义的,而 log(0+1)是 0,这正是我们对于 tf=0 所需要的。
让我们看看它是否能解决问题:
查询:‘我的一天’。
3\. Walking a dog is a good start of the **day**
Weight = log(1+0) * 2,5 + log(1+1) * 5 = 0 + 3,47 = 3,47
1\. **My** dog doesn't like all **my** pets, but me and **my** wife do love them all. A dog cannot be a cat lover
Weight = log(1+3) * 2,5 + log(1+0) * 5 = 3,47 + 0 = 3,47
2\. Cats and dogs do not like each other, but **my** cat does like dogs
Weight = 1,73
4\. Not all cats like walking, but some cats do
Weight = 0
5\. All dogs like walking, but mine doesn't like. It's like so weird
Weight = 0
我们可以看到,最相关的文档最终位于第一个位置,但它与另一个权重为 3.47 的文档共享该位置。是对还是错?这是一个很难回答的问题,因为我们永远不知道什么与请求者相关。有可能通过询问“我的一天”,他或她会认为第二个文档也是相关的,因为它给出了“我的”的许多事实,而第一个文档只说了“一天”的一个事实。因此,它们得到相同的重量可能是相当公平的。
因为这种不确定性,这可能是为什么搜索引擎通常返回的不是一个结果,而是一些数字:10 或 20 或其他,所以我们只是让用户自己做最后的决定。
结论
在这篇文章中,我简单地展示了对 TF-IDF 加权公式的思考的演变,证明了它是可行的,并且它的每个组成部分都是重要的。
还有 TF-IDF 的延续,称为 BM25,它基于前面提到的“词频饱和曲线”,特别是更多的概率方法来定义加权公式(再次记住,我们永远不知道什么是真正相关的,因此是概率方法)。总的来说,它仍然是 TF-IDF (+按文档长度规范化),但是它的根完全不同。这将在另一篇文章中讨论。
TFRecords 解释
迪莉娅·詹德妮在 Unsplash 上的照片
使用 TFRecords 并介绍 Protobufs
在这篇文章中,我将讨论 Tensorflow 记录。Tensorflow 建议以 tfRecords 格式存储和读取数据。它在内部使用协议缓冲区来序列化/反序列化数据,并以字节存储它们,因为它占用更少的空间来保存和传输大量的数据。
与 JSON 和 XML 不同,Protobufs 使用预定义的模式。tfRecords 在 Protofiles 中已经提供了这样的模式,并且还提供了许多受支持语言的编译代码。在这篇文章中,我将把这些编译好的代码导入 Python,并在我的数据中使用它们。
这篇文章需要对 Protobufs 有一个基本的了解,因为它们是 TFRecords 的组成部分。我之前也写过一篇关于使用Protobufs的帖子。在这篇文章中,我首先回顾了 tfRecords 的预定义原型,在 Python 中使用它们,并给出了一些注意事项。然后我有两个演示,我将从现有的数据集制作 tfRecords。
预定义的原型
这是取自 Tensorflow 的 feature.proto 的代码片段。完整的文件可以在这里找到。它包含 3 种原始消息类型,字节列表、浮动列表和int 64 列表。这些可以单独用来创建一个整数、浮点数或字节的列表,可以序列化为二进制。我将在下一节中对此进行更多的讨论。
下一个代码片段列出了来自上述同一文件的另外 3 个原型。它有一个特性消息类型,其中使用了 BytesList、FloatList 和 Int64List,因为它是字段之一。当您想一次只设置所有字段中的一个字段时,使用 Oneof 字段。一个中的字段共享一个公共内存。如果在一个 of 中设置了多个字段,则它会自动清除其他字段。更多关于这个的信息可以在这里找到。然后创建了一个 Features 消息类型,这是上面创建的一个字符串和一个 Feature 类型的映射。然后最后一个是例子用 Features 作为它的字段。
这些原型编译后的代码会分别给出 tf.train.Int64List,tf.train.FloatList , tf.train.BytesList , tf.train.Feature , tf.train.Features 和 tf.train.Example 。现在,让我们来看一些 Python 代码,其中我使用了这些原型的编译版本。
在 Python 中使用编译后的原型
这段代码展示了上面定义的基本原型的用法。我使用它们通过传递重复的值来创建 tf.train.Int64List、tf.train.FloatList 和 tf.train.BytesList 的实例。type 命令显示是从 tensor flow . core . example . feature _ Pb2 文件导入的,该文件是协议编译后生成的文件。
在这里,我通过传递上面创建的所有列表来创建 tf.train.Feature 的一个实例。但是当我们打印它的时候,它只显示了 float 列表,没有 int 列表或者 bytes 列表被打印出来。原因是 tf.train.Feature 将它们作为字段之一。所以只有放在最后的列表会在那里,其他的会被清除。
这意味着 Tensorflow 说我们不应该在实例化一个 tf.train.Feature 时填充多个字段,在 protofile 中使用 oneof 正好符合要求。但即使知道了这一切,我还是逼着自己去做。那我该如何防止自己再犯同样的错误呢?
嗯,我会这么做。我将为它们创建一个包装器,并在其中调用 tf.train.Feature,这只会填充其中一个字段。我需要调用这些包装方法来创建 tf.train.Feature 实例。这明确地阻止了我创建一个 tf.train.Feature 并一次填充所有这些文件。
但是我在这里面临的另一个问题是,我如何创建一个带有元素列表的 tf.train.Feature(代码中的第 18 行)。将元素列表传递给包装器会产生 tf.train.Feature 形式的 TypeError,它只接受整数、浮点或字节列表,而不接受列表列表。如果我将一个列表传递给包装器方法本身,那么它将尝试创建一个 tf.train.Feature 元素,该元素的列表包含一个整数、浮点数或字节的列表。错误也是。如果我有另外一个方法,用一个列表代替一个元素,这就可以避免。
这也是一个包装器,但是它接受一个列表并创建 tf。具有元素列表的特征。这可以用前面所有的包装器方法重复。
然后我把上面创建的 tf.train.Feature 实例放到一个 tf.train.Features 映射中,并从映射中创建一个 tf.train.Example 实例。这个 tf.train.Example 实例是我将序列化为二进制的实例。
tf.train.Example 中的 tfRecord
好了,所有的基本工作都完成了。现在我们知道如何从一组整数、浮点或字节元素中创建一个 tf.train.Example。是时候做一个 TFRecord 了。TFRecord 是指将这类记录的序列序列化为二进制。与任何其他数据格式相比,二进制格式占用更少的存储空间。
这就是我现在要做的。我将把一个数据集的所有记录转换成 TFRecords,TF records 可以序列化成二进制,也可以写入一个文件。Tensorflow 说,
- 对于记录中的每个值,创建一个 tf.train.Feature 实例。
- 将记录的每个 tf.train.Feature 实例作为值添加到映射中,以属性名(列名)作为键。它将为数据集的记录创建一个 tf.train.Features 地图
- 使用上面创建的 tf.train.Features 映射创建 tf.train.Example 的一个实例。通过此过程,创建的 tf.train.Example 实例数将与数据集中的记录数相同。
我有两个演示来展示这种方法。其中一个是 csv 文件,另一个是图像数据集。但在深入研究之前,还有一些事情需要澄清。
- 我需要一个方法来根据记录中值的类型创建适当的 tf.train.Feature。所以我在这里写了一个可以用于两个例子的方法。
- 这里的方法从数据集中获取行,并将其列类型和列名作为列表。
- 然后,它迭代该行的值,检查类型,然后创建相应的 tf.train.Feature 元素。
- 然后如上所述,将 tf.train.Feature 实例作为值添加到 tf.train.Features 映射中,将列名作为键。然后,该映射用于创建 tf.train.Example 的实例。
2.在读取序列化格式时,我需要另一种方法来解析 tfrecords。每个列都有自己的基于类型的解析机制。
- 这显示了从 tfrecord 中读取时每个属性的解析机制。
- 它将列名和列类型的映射作为键值对。
- 然后,它使用 tf.io.FixedLenFeature 配置来解析固定长度的输入特征,以及相应类型的值。这将使用作为键的列名和作为值创建的解析器填充另一个映射。
这些方法现在都没多大意义了。当我们在下面的例子中使用它们时,我们可以回到这些。
示例 1: CSV 数据集到 tfrecords
所获取的数据集是一个汽车的 csv 文件这里。它包含 8 列,第 1 列包含字符串,第 3 列和第 8 列包含整数,其他列包含浮点数。
使用列名及其类型分别作为键和值来创建映射。然后,所有的键和值都被转换为 list,以将它们发送给上面定义的第一个和第二个方法。
然后我遍历数据集的行,通过调用上面写的第一个方法获得每个行的 tf.train.Example 实例。然后我将它们序列化,并写入 tfrecords 文件。让我们看看下面的输出。序列化之前的输出只是键和值对的映射。序列化后,所有数据都是转换成字节的二进制格式,正如我所看到的,序列化后的输出很紧凑,与其他格式相比节省了很多空间。
当我需要取回数据时,我需要使用上面定义的第二种方法再次读取 tfrecords 文件。
在这里,我读取 tfrecords 文件,将其映射到 parse_example 方法,在该方法中,它实际上使用第二个方法中定义的特性描述来解析 tfrecords 文件中的每条记录。
示例 2:用于对象检测的图像到 TFRecords】
对于对象检测,我有一堆图像,它们各自的边界框存储在磁盘上的 xml 文件中。
左上和左下图像是健康的叶子。右上和右下的图像是患病的叶子。图像连同它们的边界框一起显示。labelImg 用于手动创建这些边界框,并将其放入每个图像的 XML 文件中。
这就是我解析 xml 文件的方法。Python 有一个 xml 库,它的 ElementTree 可以在这里用来提取边界框的文件名、xmin、xmax、ymin 和 ymax 以及标签(健康或不健康)。
和前面的例子一样,这里我再次创建了列名及其类型的映射,并为每个列创建了列表,将它发送给第一个方法。
我正在打开一个图像,使用 tf.image API 对它进行解码,并从 xml 中获取它的边界框细节。然后,我将创建一行对象检测所需的所有输入,并通过将它们发送给第一个方法来创建一个 tf.train.Example。然后,该示例实例被序列化并写入 tfrecords 文件。
与前面的例子相似,为了读回图像和 xml 细节的记录,这里我调用前面定义的第二个方法。得到解析的记录后,我打印它们的文件名。
这都是关于 TFRecords,以及如何将它们用于不同类型的数据集。暂时就这样了。请随时留下评论或任何改进建议。
码头工人的 1–2–3
我几乎每天都会用到的 6 个 Docker 命令。
在很长一段时间里,我都被 Docker 吓坏了(尽管它有可爱的鲸鱼标志)。关于 Docker,我所能记得的是我本科时的一次长篇大论,这让我开始质疑自己当初为什么会选择电脑。
当我终于有时间使用它时,我意识到它有点酷。也没有我记忆中的那么可怕。
Docker 是什么?
Docker 是一组平台即服务产品,使用操作系统级虚拟化来交付称为容器的软件包中的软件。容器是相互隔离的,捆绑了它们自己的软件、库和配置文件;他们可以通过明确定义的渠道相互交流。—维基百科
简而言之,Docker 是一种独立于本地设置的打包单个应用程序的方式。每个应用程序都在自己的 Docker 容器中。
到 6 个命令上。
码头工人建造
在任何带有Dockerfile
的目录中,我们都可以构建一个 Docker 映像。Docker 图像是容器的配方。我喜欢给图片加上标签(-t),这样更容易参考。
docker build . -t my_cool_app
码头运行
有了 Docker 映像,我们可以运行 Docker 容器。如果标签存在,我们可以用它来引用图像。
docker run my_cool_app
如果我们的 Docker 包含一个 web 应用程序,我们可以将它绑定到本地网络的外部端口。我们也可以给我们的容器命名。
docker run --name my_cool_container_name -p external_port:internal_port my_cool_app
Docker ps
忘了容器叫什么了?忘记我们有哪些集装箱了吗?运行docker ps
获得快速状态。
使用docker ps -a
显示以前启动的容器(不包括删除的容器)。
码头日志
日志对于调试我们的应用程序非常有用。我们需要容器名或容器 id 来查看日志。
对于运行日志,请使用:
docker logs -f [container_id or name]
忘记了容器名称或容器 id?使用docker ps
码头工人杀人
有时我们不得不与我们的容器说再见。有时,它们会占用太多内存。
docker kill [container_id or name]
码头经理
我们用它来运行运行容器中的命令。我通常用它来打开容器中的 bash 会话。
docker exec -it [container_id or name] /bin/bash
感谢您的阅读!
如果你喜欢这篇文章,可以考虑给我买杯咖啡——每一点小小的贡献都帮助我找到更多的时间在这个博客上工作。请通过 Medium 关注我的最新消息。😃
作为一个业余爱好项目,我还在www.dscrashcourse.com建立了一套全面的免费数据科学课程和练习题。
再次感谢您的阅读!📕
自学成才的机器学习工程师的 10 条戒律
使用数学、代码和数据引诱大自然揭示她的秘密的原则。
编码可能会有点灰色。用色彩包围你自己。山姆·伯克的照片。
戒律和自学这两个术语不可轻视。一个人必须对自己的教育和启蒙负责。忽略这一点,别人会为你选择。
1.数学、代码和数据是你神圣的三位一体
任何有效的机器学习管道都利用数学、代码和数据的交叉。每一个都和另一个一样有效。
如果您的数据质量很差,那么无论您的数学多么优雅或者您的代码多么高效都没有用。
如果你的数据是最高质量的,但你的数学是错误的,那么你的结果会令人失望,甚至更糟。
如果你的数据和数学是世界一流的,但你的代码是低效的,你将无法获得规模的好处。
数据为您提供了一个包含自然宝石的采矿网站。数学是你的镐。代码允许你创建一个手持鹤嘴锄的机器舰队。
三位一体是你系统的输入(你拥有的)和输出(你想要的)之间的桥梁。
注:三位一体的数学分支还包含统计和概率。如果你不喜欢这样,就把三位一体想象成一只海星。
2.三位一体的唯一例外
除了未能平衡三位一体之外,只有一个更大的罪:忘记三位一体是为谁服务的。即使是执行得最好的代码,由最优雅的数学支持,从最丰富的数据中获得洞察力,如果不能为客户服务,也毫无意义。
工程师经常会发现自己迷失在一个过程中;忘记结果。尽管带着他们最好的意图前进,他们忘记了意图没有行动重要。
一个为客户提供利益的三位一体的低性能表现要比一个什么都不提供的高性能解决方案好。
明确地说,如果你的最新模型运行 1%的准确率需要 47 倍的时间,它能提供最好的体验吗?
3.不要被三位一体所迷惑
无论你多么崇拜三位一体,你都不应该被你的爱所蒙蔽。
自学成才的机器学习工程师是他们自己最大的怀疑者。
他们知道数据不能证明,只能反证(十亿分之一的数据点就可以证明一个以前被认为是重要错误的概念),一点点糟糕的数学会产生极端的后果(自然不是线性的),代码的效率取决于它的最弱点。
无论三位一体多么神圣,一种本能的感觉都不应该被忽视。如果一个结果好得令人难以置信,除非你很幸运,否则它很可能是真的。
4.保持你与那些你寻求服务的人的关系神圣
让机器做它们擅长的事情(一遍又一遍地重复过程)。与此同时,你做你擅长的事情(关心、同情、提问、倾听、领导、教学)。
你的顾客不会像你一样关心三位一体。他们关心自己的需求是否得到满足。
透过白银法则的镜头来看待复杂的问题,比如围绕数据伦理的问题:己所不欲,勿施于人。
5.向那些为你奠定基础的人致敬
当你想到计算、机器学习、人工智能、数学这些领域时,你会想到谁的名字?
阿达·洛芙莱斯、杰弗里·辛顿、扬·勒昆、约舒阿·本吉奥、艾伦·图灵、费·李非、格蕾丝·赫柏、吴恩达、乔恩·冯·诺依曼、艾伦·凯、斯图尔特·拉塞尔、彼得·诺维格?
当然,对于你听到或记住的所有名字,有 1000 个名字做出了贡献,但却从历史书上溜走了。
后起之秀应该认识到他们之前的人所做出的巨大努力,但也应该认识到他们中的每一个人都会告诉新诞生的机器学习工程师同样的事情:该领域的未来取决于你的工作。
6.不要低估完全重写的力量
你的目标应该是第一次可靠地构建东西。但是随着你技能的提高,你可能会重新审视旧的创作,把它们拆掉,用新的视角重新创作。
自学成才的机器学习工程师明白,就像大自然一样,软件和机器学习项目永远不会完成,它们总是在运动中。数据发生变化,代码在新硬件上执行,一个天才发现了一个计算效率高、对内存依赖性低、适合大型数据集的优化器,并将其称为 Adam 。
你不仅应该接受这些变化,还应该欢迎它们。一旦它们出现,用你最好的判断来判断它们是否值得在你的系统中实现——仅仅因为某些东西是新的,并不意味着它是必需的。
7.避免成为工具妓女
编程界的一个常见趣闻是粉刷自行车棚。它讲述了一个程序员或程序员团队,担心自行车棚应该是什么颜色,而不是问一些重要的问题,比如这个棚是否真的可以存放自行车。
当然,自行车棚可以用一个有某种用途的计算机程序来替代。
在机器学习领域,你会听到 R 或 Python、TensorFlow 或 PyTorch、书籍或课程、数学或代码优先(两者都是,记住三位一体)、Spark 或 Hadoop、亚马逊 Web 服务或谷歌云平台、VSCode 或 Jupyter、Nvidia 或……实际上这里没有真正的替代方案。
所有有效的比较,但没有一个值得与另一方争论。
你应该回答的真正问题是:什么能让我以最快、最可靠的方式建立我的想法?
一旦你问自己这个问题,你会发现其他人也在问自己同样的问题。
工程师的诅咒是从工具开始寻找问题,而不是从问题开始寻找工具。只有到那时,如果合适的工具不存在,就应该构建它。
教育资源也是如此。数学、代码和数据的神圣三位一体是不变的,不管你从哪里学到它,重要的是你如何使用它。
别忘了:很多问题不用机器学习也能解决。
8.你的想法是商品
不要混淆某人根据一个好主意行动和某人窃取你的想法。你的想法在别人手里比在你脑子里更有价值。
作为一名工程师,你的职责不仅仅是构建你的想法,还要与他人交流并向他人展示他们如何从中受益。如果你缺乏这种沟通能力,你应该和有这种能力的人合作,或者寻求发展这种能力。
在一个没有人知道该相信什么的世界里,你可以通过真实让自己与众不同。诚实地面对你的创作所能提供的和你所不知道的。承认自己不知道的事情的能力是一种优势,而不是弱点。
技术好的永远赢,说谎的永远赢不了。构建技术。不要撒谎。
9.你的邻居、同事、同学和同志也在想办法
你看到别人的进步会嫉妒吗?或者你认为这是对你潜在能力的启发?
你对别人成功的感受就是你对自己的感受。
10.你不应该贪图
你应该在应用三位一体和回答你寻求服务的人的问题上寻求建立你的技能,但是你不应该带着欲望这样做。欲望诅咒你永远把未来看得太重,而不是享受你现在所拥有的。
解决渴望提高技能的方法是培养对学习的热爱。
自学成才的机器学习工程师能够快速学习驾驭数学、代码和数据能力所需的概念,但从不匆忙。他们明白学习任何有价值的手艺都需要时间,如果是这样的话,他们还不如乐在其中。
回想一下,从一开始,你就是那个对自己的启蒙和教育负责的人。知道了这一点,你就应该选择那些无论你运气如何都会领先的项目。这个项目满足你的好奇心了吗?它挑战你的技能了吗?它允许你遵守戒律吗?如果是这样,就足够了。
最后,这位自学成才的机器学习工程师在沿着自己的道路跳舞的同时,一直让他们记忆犹新:
- 没有知识就没有认证。
- 不要想太多而不做。
- 没有享受就没有学习。
- 没有风格就没有创作。
- 没有实践就没有技能。
- 没有没有目的的工具。
- 没有航运就没有炫耀。
- 没有怀疑就没有假设。
- 没有贡献就没有消费。
- 没有对现在的爱,就没有对未来的渴望。
最重要的是,三位一体没有机器学习。
本文最初是作为 一期的《吃、动、学、做——一份为饥饿、活跃、好奇的创作者准备的时事通讯。如果前一句话描述的是你,那你就 报名 。
一百万美元的问题
一个看似微不足道的问题失控了。但是你知道答案的几率有多大?这是矛盾的
介绍问题,图片作者。城市景观背景由 Pawel Nolbert
这个简单的小问题在社交媒体上被问及并被广泛分享。起初,看起来我们都知道答案,但当我们仔细观察时,我们意识到这不是那么微不足道的。
在阅读文章的其余部分之前,请在评论中告诉我们你的想法。
我们去兔子洞吧
我们都知道,如果我们有四个选择,有一个是正确的,那么正确的概率是 1/4=25%,这是正确的。因为在 A 和 D 处有两个 25%的可能性,我们有 1/2 给我们 50%作为正确答案。假设现在问题 C 是对的,随机选择它的几率还是 1/4=25%。
考虑到这个自我参照的美丽圈,许多用户选择了选项 B,为 0%。然而,假设这确实是正确答案,我们将有 1/4=25%的机会随机抽取。
再来一圈?让我们假设一个答案必须是正确的,这导致排除答案 b。这将导致我们相信我们有 1/3 的机会是正确的 33%,这不在黑板上,留给我们 0%的正确答案…你的脑袋转圈比任何 f 1 车手都快吗?
不用担心,正如我们所看到的,这只是一个不适定的问题,的确很多答案可以被视为正确的。通过问这个问题的方式,我们不能确定我们遇到的是什么随机方法。可以排除一个答案吗?这整个事情可以被看作是一个所谓的数学悖论。
悖论
有时也被称为二律背反。悖论一般指逻辑上自相矛盾的陈述。这种悖论的两个非常著名的例子是“说谎者悖论”和“罗素的悖论”
骗子的悖论:“这个说法是假的”
罗素悖论:不是自身元素的所有集合的集合不能存在
“说谎者的悖论”,举例来说,不能被赋予一个真值,因为当它为假时它就是真的,反之亦然。
“罗素悖论”,是一个理解起来稍微复杂一点的例子,过去制造了不少麻烦。
同一个问题的一个稍微容易理解的版本可以通过查看
理发师悖论:理发师给所有不刮胡子的人剃毛
理发师现在应该给自己刮胡子吗?
真实悖论的主要问题在于,当集合不包含自身时,它会包含自身。这个看似简短的声明让当时最聪明的人怀疑自己和他们选择的研究领域。
这个悖论以伯特兰·罗素的名字命名,并于 1901 年由他发表,它造成了相当多的问题,因为它可以从他们的轴中导出。
为什么会有问题?
首先,我们必须明白数学是建立在公理之上的。从这些公理出发,其他一切都被证明了。公理是被认为是正确的陈述。它们是所有基于它们的推理的基础。最早的例子之一来自古希腊。
当从相等的数中取出相等的数时,结果是相等的数。
从这个公理出发,我们可以证明一些简单的事情,比如:
if a=b -> a-42=b-42 (not entirely formal ;)
我们需要了解的另一件小事是爆炸的 p 原理。爆炸原理指出,从一个错误的陈述,任何事情都可以被证明。比如说我们可以。
P1: You follow me and you don't follow me
P2: You follow me *(1, simplification since both are true)*
P3: You dont' follow me *(1, simplification)*
P4: You follow me **or** You will clapp 100 times for this article
*(Since we can add anything to a already true* ***OR*** *statement and it will still be true, also called addition)* P5: You will clapp 100 times for this article
*(Since P3 is true and P4 is true, we know that the second part of P4 must be true, aka* disjunctive syllogism*)*
悖论和爆炸原理的结合现在基本上打破了数学、科学和一般的推理。它给我们留下了一个非常模糊的真假定义。当时数学家的成果是创造了一种新的一致的集合论。
结论
干得好,你今天用一些最棘手的问题挑战了自己。这些例子中的每一个都让你更加怀疑我们可以用什么和怎样来表达一个问题!
自从人类提出奇怪的问题以来,悖论就一直存在。他们的存在给我们上了宝贵的一课;有时候,不是答案不对。这是个问题!
全世界都在研究悖论,这是有原因的,它们对发展批判性思维至关重要。我希望你现在可以停止担心它,并接受一些问题确实不需要回答,假设你想保持理智。
如果你喜欢这篇文章,我会很高兴在 Twitter 或 LinkedIn 上联系你。
一定要看看我的 YouTube 频道,我每周都会在那里发布新视频。
在 Google Colab 上传图片的两步指南!
在这个简短的教程中,我们将学习如何从您的磁盘上传到 Google 协同实验室。
很多时候,除了我们的代码和文本之外,我们还想在 Google Colab 中上传一张图片,以包含数学推导或方程。在做我的作业时,我发现网上没有一个有效的来源可以找到答案。因此,我决定与你们分享做同样事情的最简单的方法。
第一步:将图片上传到 google drive &复制可分享的链接。
首先,打开 google drive &将图片上传到硬盘上。
选择上传的图片,点击右键,获得一个可共享的链接&复制它。
第二步:上传到 Google Colab
打开谷歌 Colab 笔记本&在你想包含图片的地方添加文本块。
下面给出了包含图像的通用代码。将之前复制的图片的共享链接粘贴到圆括号中。
粘贴链接后,替换‘打开?’ 同‘UC?’
按“Shift+Enter”
你有它!我们只需两步就能在 Google Colab 中包含一张图片。
如果你觉得这篇文章有用,请为它鼓掌,它会有很大的帮助!!
关注我 中 获取更多此类教程&文章。
如果你愿意,请在 LinkedIn 上联系我。
如果你知道其他方法,请告诉我。谢谢大家!
2019 年我看的 200 篇深度学习论文
半年前,我在 Medium 上写了我的第一篇文章,讲述了我的日常纸质阅读之旅,作为实现我新年决心的一部分。当时,我得出结论,这种每天阅读论文的活动对于保持我的思维活跃和了解深度学习领域的最新进展至关重要。现在,在 2020 年新年前夕,我可以自豪地说,我出色地执行了我的 2019 年新年决心“每周至少阅读一篇新论文”。下半年看了 116 篇,做了有条理的笔记。如果你感兴趣,这些论文的结构笔记列在下面的 Github repo 中。
这个知识库包含了我关于深度学习和机器学习的论文阅读笔记。它的灵感来自丹尼·布里兹…
github.com](https://github.com/patrick-llgc/Learning-Deep-Learning)
2019 年我读了什么?
加上上半年的 76 篇论文,我在 2019 年阅读的论文总数是 192 篇(包括我在写笔记时浏览过的论文这个数字应该是 200 多一点)。这非常接近于每个工作日一份文件 的延伸目标。当然,我有时会偷懒,或者没有遵守早起完成一篇论文的纪律,但我会在周末努力弥补。为了更好地了解我的阅读习惯,我做了一个快速可视化,灵感来自 ICLR OpenReview 可视化。生成图表的代码,包括论文的标题,这里是这里是。
200 篇论文中约有 140 篇是同行评审的出版物,其余是 ArXiv 预印本。按发表年份来看,约一半的同行评议论文来自 2019 年这一年内,最早的一篇可追溯到 2012 年。(如果你想知道,2012 年唯一的出版物是 2012 年 CVPR 的原始 KITTI 数据集论文。)
截至发稿地点,他们中的三分之一来自 CVPR,加上 ICCV,他们占了我阅读的论文的一半左右。我发现 CVPR 的论文更注重应用,而 NIPS 和 ICLR 的论文对我的口味和我关注的自动驾驶领域来说有点太理论化了。有趣的是,我经常在 NeurIPS/ICLR 的研讨会论文中发现许多隐藏的宝石。
在 2019 年下半年,除了 12 月份,我一直在严格遵守我的延伸目标。因此,每个工作日阅读一篇论文的任务是可行的,但可能有一个警告(见下文)。
对我有用的东西
- 坚持早起,早上看报纸。这是一个很好的唤醒练习,让我为一天做更好的准备。
- 按题目看论文。这有助于避免过于频繁的上下文切换,并在阅读相关领域的论文时保持良好的连续性。此外,这也促使我就这些话题写迷你评论博客。2019 年下半年,我已经在 Medium 上写了屈指可数的评论博客。
- 坚持写博客,从小处着手,专注于一个小领域,然后扩展到更广泛的话题。当我意识到我已经在 9 月份之前阅读了相当多关于单目 3D 物体检测的论文时,我决定写一篇关于它的评论,至少作为我的记录。然而,这是一项艰巨的任务,因为最近在 2019 年发表了如此多的论文(实际上关于这一主题的大多数论文都是回顾性的)。所以我从小处着手写了一篇关于用于回归物体方向的多面元损失方法的文章( 多模态目标回归 )。然后,我意识到文献中的偏航方向术语非常混乱,所以我开始了另一个帖子,这可能是最不专业的,但主要是概念性的( 单目 3D 对象检测中的方向估计 )。在这两篇帖子之后,我觉得更适合将我下一篇帖子的主题扩展到在后处理中将 2D 提升到 3D 的研究的特定方法(在自动驾驶中 将 2D 物体检测提升到 3D)。最后,当我觉得自己已经充分掌握了这一领域的趋势时,我在 11 月底写了一篇综述,总结了解决看似不适定的单目 3D 物体检测问题的不同方法( 自动驾驶中的单目 3D 物体检测——综述 )。
需要改进的地方
- Read, but also skim. I still tend to get bogged down in details. It is my habit to dive directly into papers in a linear manner with the same amount of attention to detail. Professor Srinivasan Keshav from Cambridge University has written a short article on how to read a scientific paper in three passes (Summary in Chinese can be found on 知乎). MIT also has a guideline on reading paper under time constraints. I should definitely start practicing these more disciplined methods to guide me to decide when to skim through and when to dive deep. Reading one paper a day in full detail is not sustainable for me as a software engineer — although this may be doable and even make sense to do so for full-time researchers.
麻省理工学院如何在时间限制下阅读论文的图解指南
- 质量重于数量。看高质量的论文,多花时间在高质量的论文上。低质量的论文经常有相互矛盾或不确定的结果(可能是由于过早和不充分的实验)。这尤其难以培养对特定主题的系统和连贯的理解。如何辨认这些文件?首先,我应该有一份已知能产生高质量论文的作者和研究小组的观察名单。其次,我应该浏览主要会议的高分或最佳论文提名。第三,阅读稍微老一点但有开创性的论文(一两年前的),有很多引用。第四,找到发布了源代码的论文,读取代码。
- 说话便宜,给我看看代码。 多花点时间在 Github 上看源代码。我多次意识到,代码并不总是反映论文所宣称的内容。我的 2020 新年决心应该是,1)每个月阅读和玩一个高质量的 GitHub repo 并写关于读码的复习笔记;2)每月三遍深入阅读 10 篇论文(每篇需要几个小时)。这大约是我在深度学习纸堆中犁过的速度的一半,这应该给我更多的时间来消化。
展望 2020 年
回顾 2019 年这一年,例行论文阅读的习惯真的重塑了我生活的很多方面。很多个早晨,我都迫不及待地从床上爬起来,一头扎进我一直渴望阅读的报纸——这种感觉就像你在网飞疯狂观看一些精彩节目时的感觉。这些论文为我的日常项目提供了许多新的想法,并证实了我自己的许多想法,从而使我对它们更有信心。
最后但同样重要的是,我感谢机器学习和计算机视觉社区让这个领域成为人们分享思想的开放平台。ArXiv 和 Github 可能是促成这一波人工智能革命的两个最大的催化剂。我也感到真正的幸运,能够在这个领域工作,将许多最新的研究成果转化为产品。在 2020 年的新的一年,我期待深度学习及其在自动驾驶中的应用取得更多令人兴奋的进展。
促进您分析职业发展的 25 个数据科学训练营和课程
我涵盖了 4 种不同类型的训练营,分别面向完全的初学者和有经验的专业人士
无论你是一个完全的初学者还是经验丰富的专业人士,都有数百种选择供你发展职业生涯或学习新的分析技能。有些是面授课程,花费数千美元,有些是在线课程,免费。
在本文中,我将列出 25 个数据科学训练营和项目,并将它们分为 4 种不同的类型。我将介绍大学组织的大型综合训练营,以及完全在线的小型独立项目。
关键是,根据你的经验水平、时间投入和目标,总会有适合你的东西。
不幸的是,我不会描述他们每一个人,就像我上过他们的课一样。这个列表来自网上调查和参加过这些课程的朋友的反馈。
unsplash.com
不同类型的教育课程和训练营
在我们开始之前,重要的是要注意,你可以通过各种方式建立你的教育。有四种类型的数据科学课程:
- 亲临大学附属训练营
- 面对面的独立训练营
- 教授全面数据科学概念的在线训练营
- 侧重于一套特定分析技能的在线课程
unsplash.com
亲临大学附属训练营
关于在哪里接受教育,你可能首先想到的是学院和大学。这些正式的机构是学习你今天取得成功所需技能的最好地方,许多机构提供以他们名字命名的项目和训练营。他们的品牌和名字吸引了许多学生,但也为招聘教师设立了高标准。所以你基本上是在和一些最聪明的学生一起向最优秀的人学习。
我的建议是在你所在的地区查找一所评价良好的大学。很难给出具体的建议,因为如果你想要亲身体验,这在很大程度上取决于一个机构在地理位置上是否方便。
这里列出了一些附属于大学的数据科学和分析项目/训练营。
-
哥大工程—应用数据科学 https://execed.cvn.columbia.edu/applied-data-science
-
斯坦福工程—数据科学基础 https://online . Stanford . edu/programs/Foundations-Data-Science
-
乔治敦大学继续教育学院—数据科学证书 https://SCS . George town . edu/programs/375/Certificate-in-Data-Science/
-
蒙特利尔信息技术学院 https://www.montrealcollege.ca/
-
多伦多大学继续教育学院 https://learn.utoronto.ca/
面对面的独立训练营
近年来,数据科学和分析训练营越来越受欢迎,部分原因是公司做出更多数据驱动的决策,导致对具有分析思维的思想家的需求增加。谁知道跟着感觉走有时会导致糟糕的商业决策。
由于受欢迎的程度,出现了现场训练营的热潮。许多要求你全天关注,但有些在下班后提供夜校。这些项目可能没有他们的大学竞争对手的名字和品牌,但一些像大会和熨斗学校已经如此受欢迎,他们的品牌与一些大学项目不相上下。
不利的一面是,它们可能和大学项目一样昂贵。然而,大多数都以较低的价格提供训练营的在线版本。这里列出了一些提供现场和在线数据科学和数据分析教育的热门训练营。
- 大会 https://generalassemb.ly/
- 镀锌 https://www.galvanize.com/
- 熨斗学校 https://flatironschool.com/
- 脑站 https://www.brainstation.io/
- 【https://www.codingdojo.com/】编码道场
编码道场 - 贵族桌面 https://www.nobledesktop.com/
- 铁钻营 https://www.ironhack.com/en
- 开发大师 https://www.thedevmasters.com/
- 纽约数据科学院 https://nycdatascience.com/
stratascratch.com
教授全面数据科学概念的在线训练营
当然,正如当今世界的方式一样,许多更受欢迎的选择可以通过在线教程和课程获得。虽然没有正式隶属于学术机构,但这些课程在向你提供知识方面往往同样出色。因为他们完全在线,他们完全自定进度。
作为一个有几年分析经验的人,我喜欢这些在线课程,要么学习新的东西,要么练习我不擅长的技能。在完全沉浸在面对面的项目中之前,我通常会先选择这个选项,看看我是否喜欢它。
值得注意的是,有些是免费资源,有些确实要花钱。在你开始之前,确定你愿意花多少钱。
- Udemy https://www.udemy.com/
- 数据科学道场 https://datasciencedojo.com/
- K2 数据科学
T34http://www.k2datascience.com/ - 拉姆达学派 https://lambdaschool.com/
- 【https://www.thinkful.com/
T42 - 数据营 https://www.datacamp.com/
专注于特定分析技能的在线项目
在我看来,我把最好的留到了最后。
专注于一套分析和数据科学技能的在线课程通常最擅长教授这些技能。
这些在线课程通常比我上面提到的其他三个类别更便宜,我可以挑选我想学习或更擅长的技能——所以这对我来说是一个双赢的局面。
对于那些经验丰富或具有分析基础,但希望为即将到来的技术面试或只是在当前的数据科学工作中做得更好而练习的人,有一些在线资源可以让你做到这一点,而不必经历漫长、艰苦的训练营。我以前写过这些“利基”在线课程,但这里有一些我最喜欢的,并简要描述了它们的重点。
- Strata Scratch — 500+来自真实公司的 SQL 和 python 面试问题,将帮助你准备技术面试或提高你的分析能力
https://ww.stratascratch.com - leet code——主要面向开发人员,但他们有一个不错的数据库部分,有大约 40 个 SQL 问题
https://www.leetcode.com - HackerRank——主要是为了帮助公司提供技术筛选,hacker rank 确实有一套很好的 python 和 SQL 问题来帮助提高你的技能
https://www.hackerrank.com
结论
现在你知道了,根据你的经验、时间投入、预算和目标,在你人生的任何阶段都有很多培养技能的方法。
你可以从上面的选项中获得很好的教育,但我认为最好的课程选项是适合你学习风格的。确定你喜欢如何学习,你是否喜欢更多的结构,更多的实践机会,更多的自由,或者你需要的任何东西。选择适合自己的课程是良好学习环境中最重要的因素。
学习 MLOps 的 3 个最佳免费在线资源
由 Unsplash 上的 chuttersnap 拍摄
如何开始学习需求最高的数据科学技能之一
如果你参加一个普通的机器学习或数据科学课程,你可能只会听到算法。有些更实用,教你如何使用某些框架和训练模型,但大多数不会超出这个范围。
然而,这只是整个机器学习管道中的一小部分。作为一名工程师或数据科学家,你的任务很少以方法开发开始和结束。更确切地说,大多数时间花在服务于基础设施管理的数据工程和模型上。
机器学习系统的结构。来源:机器学习系统中隐藏的技术债务,D. Sculley 等人。
随着专业人士社区很快意识到这一点,越来越多的努力被用于在整个生命周期中管理机器学习操作。因此,用 DevOps 来类比,MLOps 的领域已经慢慢出现了。
在一个技术领域的发展过程中,其可达性经历了三个主要阶段。首先,在开始时,如果你不在努力的最前线,知识是不容易获得的。第二,编写第一批教科书和开设第一批课程的地方,但最佳做法仍然不明确,信息分散在几个地方。最后,当一个领域成为标准课程的一部分时,它就达到了一定的成熟度。深度学习和机器学习已经有了。
然而,MLOps 仍处于第二阶段。有几个很好的学习资源,但是要找到和过滤它们需要相当长的时间。这篇文章旨在为你做这项工作:我们将看看学习 MLOps 基础知识的三个最好的地方。
我们开始吧!
1.全栈深度学习课程
最初在伯克利的新兵训练营教授的全栈深度学习课程已经成为对机器学习更实用方面最全面的介绍之一。
最近,他们将整个系列讲座和专题一起放在了网上
来源:全栈深度学习课程
他们的课程不是理论和模型培训,而是包含以下讲座:
- 建立机器学习项目
- 基础设施和工具
- 数据管理
- 机器学习团队
- 培训和调试
- 测试和部署
总的来说,这是我认为对该领域最好的介绍。所教授的内容是广度而非深度,但最终,你会意识到这是一个多么广阔的领域,你有多少是不知道的。
2.安德烈·布尔科夫的机器学习工程
机器学习工程书籍由安德烈·布尔科夫撰写,完美补充了全栈深度学习课程。这本书本身是根据“先阅读,后购买”的原则发行的,这意味着如果它为你提供了价值,你可以通过购买来支持作者。
这本书没有进入 MLOps 的工具包,而是提供了更多的“实践理论”方法,为您提供了机器学习问题的问题、疑问和最佳实践的概述。
如果你感兴趣,你应该看看《T21》这本 100 页的机器学习书籍,这是同一作者写的一本更注重理论的书。
3.令人敬畏的 MLOps 和生产机器学习 GitHub 列表
也许你以前遇到过这个概念,但是如果这是第一次,那么 Awesome list 是一个主题管理的资源目录,以只包含一个 README 文件的 GitHub 存储库的形式托管。
在我们的例子中,两个非常有用的列表是 Awesome MLOps 和Awesome Production Machine Learning。前者侧重于学习资源,后者则通过强调工具来补充。
当您已经对 MLOps 字段有了一个全面的了解,并且希望专攻给定的子域(如模型服务和监控)时,这些列表非常有用。
正如你已经看到的,在整个生命周期中管理机器学习项目是非常复杂的。然而,有了这些知识,你就能准备好应对许多挑战。所以,去学牛逼的东西吧:)
如果这些资源对你来说还不够,还有很多其他的。查看 neptune.ai 的这篇文章,在这里你可以找到大量有用的建议!
如果你喜欢把机器学习概念拆开,理解是什么让它们运转,我们有很多共同点。看看我的博客,我经常在那里发表这样的技术文章!
营销分析的 3 个最重要的技能
数字营销是所有业务中数据最丰富的领域,也是陷阱最多的领域。
也许比任何其他领域,营销,尤其是数字营销,几乎完全围绕着数据。这使得作为分析师或数据科学家,这是一项丰富且值得支持的业务,因为数据的数量和效用可能非常高,增加了分析专业人员的需求和潜在项目的范围。
然而,营销数据有一些重要的陷阱,会使营销分析程序脱轨:
- 从广告平台、社交和点击流数据到 CRM 和 CDP 数据,营销数据分散在众多来源中。
- 如果没有分析团队和营销团队都理解的有凝聚力的标记和跟踪策略,整合点击流和营销数据是一项挑战。整合销售和营销数据,尤其是在 B2B 中,很少能正确完成。
- 营销团队有十几种不同的方法来衡量营销绩效,这意味着创建仪表板和分析是一项挑战。
在这篇文章中,我将解释 3 个关键技能,它们将帮助你克服这些陷阱。
1.使用 API
SQL 和数据操作技能不足以有效地获得衡量营销计划所需的所有数据——除非您想每天手动下载 excel 电子表格。您需要学习如何使用 REST APIs 编码,以自动获取广告平台/点击流/和其他营销数据。
你可能听过的最大的谎言是,你需要成为一名开发人员来使用 API。事实是:你不知道。这里有一些提示和技巧,可以帮助你达到 80%的目标:
- 阅读我关于如何使用 Python 请求从 API 中提取数据的文章**。本文回顾了如何使用 Python 请求包从 Microsoft Graph API 中提取电子邮件数据。那篇文章中的方法将扩展到使用广告平台 API,如脸书图形 API 或 Linkedin 广告 API。
- 了解 PYODBC 包。PYODBC 允许您使用 Python 编写 SQL 语句和处理数据库。我使用 PYODBC 将从各种 API 提取的数据插入到数据库中。
- 使用邮递员。Postman 是一个使用 API 的 GUI 应用程序。Postman 允许您在 GUI 中输入值,并以拖放/点击的方式与 API 交互。一旦在 Postman 中成功提取了数据,就可以在 Python 中导出 API 调用,然后将代码片段放入脚本中。
- 如果你不想处理任何编码,并且有多余的预算,我推荐你使用 Stitch 或者 Xplenty。这些 ETL 平台内置了与大多数主要 API 的集成,允许您无需任何代码就可以将数据移入数据库。这些平台的缺点是成本——但是如果你有预算,我强烈推荐这些平台作为维护 ETL 管道的替代方案。
2.了解网络分析、点击流数据和标签
你的网站很可能是你营销活动中发送流量和点击量的主要地方。随后,要讲述一个完整的营销故事,你需要了解网络分析、点击流数据,以及这些数据如何与你正在制作的广告联系起来。
下面是一些原始点击流数据的简单示例:
您可以看到有一个时间戳、一个唯一标识符、URL 和一个 SDID 列,其中包含来自 URL 跟踪参数的唯一活动标识符(跟踪参数是“?”后面的值)在 URL 中)。
当在广告平台(脸书、Linkedin、Twitter 等)上创建活动时。)—来自这些活动或广告的信息需要与定向流量的 URL 联系起来。这通常发生在 URL 跟踪参数上,但是你会惊讶有多少营销团队要么 a)没有标记任何东西,要么 b)不一致或不正确地标记广告。标签和分析之间的任何差异都将意味着你试图收集的数据存在巨大差距,这将使有效衡量营销活动变得极具挑战性或不可能。
由于这一过程的复杂性,我总是建议分析师/数据科学家/任何将负责营销计划衡量的人,参与或创建自己的跟踪和标记策略。通常,营销团队或代理会创建标记策略,但由于对数据收集和清理流程的误解,这往往会遗漏有价值的数据(例如,在多个广告上使用相同的唯一标识符将导致无法判断哪个广告对流量/转化/等负责)。).当分析师自己拥有这一过程时,最终获得干净的数据将会容易得多。
作者图片
上述过程很少发生,即使是在最大的高科技公司的营销团队中。通常,营销人员会使用 Adobe Analytics 或 Google Analytics 仪表盘,或者默认使用特定平台的分析。这有时会奏效——尤其是如果你主要是一家电子商务公司。当您希望将所有数据捆绑在一起时,问题就出现了——目前这只能由理解我上面讨论的动态的分析专家来完成。B2B 业务尤其容易受到这个问题的影响,因为许多 B2B 销售是在会议室而不是网上进行的。因此,要真正衡量营销对销售的影响,你必须以某种方式将营销数据与 CRM 数据联系起来——如果没有合适的分析人才,这是非常具有挑战性的——也就是说,有人已经学会了这些关键技能。
3.丰富的商业和营销领域知识
一旦编译和集成了干净的数据,就可以让营销和销售团队消化这些数据了。这可能是所有技能中最困难和最微妙的,因为它要求你跳出技术分析和数据工作,花时间与业务和营销从业者在一起。在创建能够有效传达营销绩效的仪表板或报告之前,您需要明确一些事情:
- 营销团队实际关心的是什么?永远不要假设你的营销团队的优先事项是什么。你需要非常清楚和你一起工作的营销人员的成功标准是什么。如果他们不知道,那么你就必须创造他们,这意味着充分了解营销计划和业务,以说服 KPI 团队有意义。
- 他们认为什么是成功?即使您的营销团队已经向您提供了他们想要查看的成功指标,他们也总是希望将其与某些指标进行比较,无论是行业标准的绩效指标还是时间驱动的指标(例如,去年这个时候、去年至今,等等)。)永远不要创建不允许与标准或时间序列进行比较的仪表板。
- 失败可以吗?这很不幸,但是我合作过的几个营销团队只关心那些让他们的项目看起来不错的数据。您需要从一开始就知道这一点——团队的目的是测量和改进还是仅仅呈现信息?许多团队只是想向利益相关者展示他们花了预算,带来了大量流量——他们并不真正关心如何制作更好的广告。根据你所合作的营销团队的类型,你将制作两个完全不同的仪表板/报告。
营销人员最好的朋友
我上面概述的三个技能对于做好营销分析是绝对重要的。没有它们,你将无法有效地衡量和报告公司的营销计划。有了它们,你将成为一笔不可多得的财富,迅速成为营销人员最好的朋友。
有问题或意见吗?你可以给我发电子邮件,地址是 cwarren@stitcher.tech,或者在 Linkedin 上关注我,地址是https://www.linkedin.com/in/cameronwarren/
我还提供营销分析服务。如果您想了解更多信息,请直接发邮件至 cwarren@stitcher.tech 或前往 http://stitcher.tech/contact/。
如果你想要帮助连接你的点击流和客户关系管理数据,看看 https://stokedata.com/的。
3 个最重要的基本分类指标
超出准确度
这是帮助您理解、使用和记住七个最常见的分类标准的系列文章中的第二篇。
在数据科学和相关领域,人们认为从业者理解准确性和混淆矩阵。如果你没有,一定要看看我之前的文章这里。它应该有助于驱散迷雾。☁️
在本文中,我们将深入探讨每个数据科学家和统计学家都应该知道的三个最重要的基本分类指标。🚀
三瓶津宁。资料来源:pixabay.com
我将我们将要探索的三个指标称为基本 指标,因为每一个指标都由混淆矩阵的一个象限除以该象限再加上另一个象限组成。
当有两种可能的结果时,我们关注二元分类的情况。
我们开始吧!🚀
召回率(又名灵敏度,真阳性率,检测概率,命中率,等等!)
最常见的基本指标通常被称为回忆或灵敏度。它更具描述性的名字是 t rue 阳性率(TPR)。我称之为召回。
当你真的想正确预测真实类中的案例时,回忆是很重要的。例如,如果你有一种危险形式的癌症的测试,你真的希望这种测试能很好地检测出所有人实际患有癌症的情况。所以你真的很在乎回忆。
召回率的计算方法是将真阳性除以真阳性加上假阴性:
召回= TP / (TP + FN)
换句话说,在所有真实案例中,你的模型预测正确的比例是多少?
以下是我们的模型预测网站访问者是否会在 Jeff 的 Awesome Hawaiian Shirt 商店购买衬衫的结果。🌺👕
Predicted Positive Predicted Negative
Actual Positive 80 (TP) 20 (FN)
Actual Negative 50 (FP) 50 (TN)
使用我们的混淆矩阵示例,什么是回忆?
80/(80 + 20) = 80%
该模型正确预测了五分之四的销售额。听起来不错!😀我们可以将我们的模型的召回率与另一个模型的召回率进行比较,以帮助我们选择要用于预测的模型。
最佳召回率是 1,最差召回率是 0。scikit-learn 函数名为recall _ score。
对于回忆真的很重要的情况,我们可以做些别的事情来正确预测更多的真实情况:我们可以改变我们的决策阈值。
三津宁视角的转变:来源:pixabay.com
决策阈值
默认情况下,scikit-learn 分类模型的决策阈值设置为. 5。这意味着,如果模型认为某个观察值有 50%或更大的概率成为正类的成员,则该观察值被预测为正类的成员。
如果我们非常关心召回,我们可以降低我们的决策阈值,以试图捕捉更多的实际阳性病例。例如,您可能希望模型以 30%或更高的概率预测每个观察值为真。
目前在 scikit-learn 中还没有快速的方法来做到这一点。这个堆栈溢出答案解释了你如何用一个定制的预测方法来获得结果。(修改 2020-05-06 0 帽子尖给凯文·马卡姆)
这一变化可能会将一些假阴性转化为真阳性。耶!🎉然而,这个模型也会把一些真阴性变成假阳性。嘘!😢
毕竟,通过预测每个观察都是积极的,你可以得到完美的 100%回忆。但这通常不是一个好计划。
当误报的代价很高时,你要注意它们。您需要一个度量标准来捕捉您的模型区分真阳性和假阳性的能力。你需要注意精度。
没错。资料来源:pixabay.com
精确
精度是相对于所有肯定预测,有多少肯定预测是正确的比率。它回答了这个问题
正面预测的正确率是多少?
精度= TP / (TP + FP)
我通过关注字母 p. 的头韵来精确记忆
P 精度是所有的真值 P 正数除以所有的 P 预测值 P 正数。
这又是夏威夷衬衫销售混淆矩阵:
Predicted Positive Predicted Negative
Actual Positive 80 (TP) 20 (FN)
Actual Negative 50 (FP) 50 (TN)
精度分数是多少?
80/(80+50) = 61.5%
scikit-learn 的度量是precision _ score。语法类似于 recall 的。****
**precision_score(y_test, predictions)**
同样,最佳值是 1 (100%),最差值是 0。
人们经常根据精确度与回忆的关系来讨论精确度。事实上,scikit-learn 中有一个plot _ precision _ recall _ curve函数,我们可以使用它来可视化精度和召回之间的权衡。**
以下是使用一些大型数据集在逻辑回归模型上绘制精确召回曲线的结果:
AP 代表平均精度,这是精度-召回曲线下的区域。越高越好,最大可能值为 1。
绘制该图的代码是:
**plot_precision_recall_curve(lr, X_test, y_test);**
该图显示了不同决策阈值下的精确度和召回率。请注意,查全率随着精度的下降而上升。📉
如果我们将决策阈值设置得更低,我们将沿着曲线向右移动。更多的观察将被归类为阳性类,我们将有希望捕捉到更多真正的阳性病例。召回会上去。😀
然而,我们也会有更多的误报。这将使精度的分母变大。结果会降低精度。☹️
我们愿意容忍多少假阳性取决于假阳性的成本相对于真阳性的成本有多大。这是平衡之举!
有时我们关心我们的模型对实际负面因素的预测程度。让我们来看看这种情况的衡量标准。
如果你需要直升机救援,这是否定的。资料来源:pixabay.com
特异性(真阴性率)
特异性也被称为真阴性率 (TNR)。它回答了这个问题:**
我的模型捕捉负面案例的能力如何?
这是特异性的公式:
特异性= TN / (TN + FP)
请注意,特异性只与实际的阴性情况有关。
这是夏威夷衬衫销售混乱矩阵。
**Predicted Positive Predicted Negative
Actual Positive 80 (TP) 20 (FN)
Actual Negative 50 (FP) 50 (TN)**
我们模型的特殊性是什么?
50 / (50 + 50) = 50%
特异性可以从 0 到 1,所以 50%不是很大。🙁该模型没有很好地正确预测何时有人不会购买。
当正确预测实际的负面影响很重要时,特异性是一个很好的指标。例如,如果对一种疾病的治疗是危险的,你需要高度的特异性。👍
特异性通常与敏感性一起讨论。请记住,灵敏度的名称是,回想一下和真阳性率。****
Scikit-learn 没有名为特异性的内置函数。您可以从混淆矩阵中创建四个结果变量,并按如下方式计算特异性:**
**import numpy as np
from sklearn.metrics import confusion_matrixtn, fp, fn, tp = confusion_matrix(y_test, predictions).ravel()
tn / (tn + fp)**
或者,如果你的负类为零,你可以使用 recall_score 和 pass pos_label=0 。😉
特异性是你的工具带中需要的最后一个基本分类标准。🔧
概述
你已经学会了回忆、精确和具体。请记住,回忆也被称为敏感度或真实阳性率。特异性也称为真阴性率。
有了准确性、回忆性、精确性和特异性,您就有了需要了解的基本分类术语!
我希望这篇关于基本分类标准的介绍对您有所帮助。如果你有,请在你最喜欢的社交媒体上分享,这样其他人也可以找到它。😀
在本系列的最后一篇文章中,我们将探讨三个最重要的复合指标。它们稍微复杂一点,但是它们在一个数字中传达了很多信息。🚀
我撰写关于 Python 、 SQL 、 Docker 以及其他技术主题的文章。如果您对此感兴趣,请注册我的数据科学资源邮件列表,并在此阅读更多。👍
很多山峰。资料来源:pixabay.com
快乐平衡!⚖️
3 个最重要的综合分类指标
复合分类度量帮助您和其他决策者快速评估模型的质量。它们通常比简单的指标(如召回率、精确度或特异性)提供更有价值的信息。
数据科学家和统计学家应该了解最常见的复合分类指标。本指南将帮助你保持直线。🎉
花是一种合成物。资料来源:pixabay.com
这是帮助您理解、使用和记住七个最流行的分类标准的系列文章中的第三篇,也是最后一篇。在本系列的第一篇文章中,我解释了混淆矩阵和最常见的评估术语:准确性。在的第二篇文章中,我强调了三个最常见的基本指标:召回 ( 灵敏度)、精度和特异性。如果你还没有完全理解这些术语,我建议你在继续之前多花些时间了解它们。👍
本文中的每个复合指标都是从基本指标构建的。让我们来看看一些漂亮的复合指标!
平衡精度
正如您在本系列的第一篇文章中看到的,当结果类别不平衡时,准确性可能会产生误导。
平衡精度是用于不平衡数据的更好指标。它考虑了积极和消极的结果类别,并且不会因不平衡的数据而产生误导。
公式如下:
平衡精度=(((TP/(TP+FN)+(TN/(TN+FP)))/2
回想一下上一篇文章,哪个度量是 TP/(TP+FN) 的公式?没错,召回——也叫灵敏度和真阳性率!
并且哪个度量是 TN/(TN+FP) 的公式?没错,特异性,又称真阴性率!
因此,这里有一种更简单的方法来编写平衡精度公式:
平衡准确度 =(灵敏度+特异性)/ 2
平衡的准确性只是敏感性和特异性的平均值。当它们同等重要时使用是很棒的。☝️
让我们继续本系列前几篇文章中的一个例子。以下是我们的模型预测网站访问者是否会在 Jeff 的 Awesome Hawaiian Shirt 商店购买衬衫的结果。🌺👕
Predicted Positive Predicted Negative
Actual Positive 80 (TP) 20 (FN)
Actual Negative 50 (FP) 50 (TN)
我们的灵敏度是 0.8,特异性是 0.5。平均这些分数以获得我们平衡的准确度:
(.8 + .5) / 2 = .65
在这种情况下,我们的准确率也是 65%:(80+50)/200。
当结果类的大小相同时,精确度和平衡精确度是相同的!😀
岩石平衡:来源:pixabay.com
现在让我们看看不平衡的数据会发生什么。让我们看看我们以前的疾病检测的例子,阴性病例多于阳性病例。
Predicted Positive Predicted Negative
Actual Positive 1 8
Actual Negative 2 989
我们的准确率是 99%:(990/1000)。
但是我们的平衡准确率是 55.5%!
(((1/(1 + 8)) + ( 989/(2 + 989))) / 2 = 55.5%
😲
您认为 55.5%的平衡准确度比 99.0%的准确度更能体现模型的性能吗?
平衡精度底线
当您有不平衡的数据,并且在正确预测负类和正类之间没有区别时,平衡精度是一个很好的衡量标准。😀
scikit-learn 函数名为balanced _ accuracy _ score。
另一个更常见的综合指标是 F1 分数。
公式 1:资料来源:pixabay.com
F1 分数
F1 得分是精度和召回的调和平均值。如果您关心精确度和回忆大致相同的数量,F1 分数是一个很好的指标。请注意,尽管您看到的所有指标后面都可以跟有单词 score F1,但它始终是。☝️**
请记住,回忆也被称为敏感度或真阳性率。
以下是 F1 得分的公式,分别使用 P 和 R 表示精确度和召回率:
*****F1**= 2 (P * R)/(P+R)
让我们看看我们所看的两个例子在 F1 分数方面是如何比较的。在我们的夏威夷衬衫的例子中,我们的模型的召回率是 80%,精确度是 61.5%
该车型的 F1 分数为:
2 * (.615 * .80) / (.615 + .80) = .695
听起来没那么糟糕。🙂
让我们计算疾病检测示例的 F1。该模型的召回率为 11.1%,准确率为 33.3%。
该型号的 F1 为:
2 * (.111 * .333) / (.111 + .333) = .167
那不是很热。☹
比我们模特的 F1 分数高多了。资料来源:pixabay.com
F1 评分很受欢迎,因为它将两个通常非常重要的指标(召回率和精确度)结合成一个指标。如果其中任何一个低,F1 分数也会相当低。
scikit-learn 函数名为f1 _ score。让我们看看最后一个流行的复合指标,ROC AUC。
ROC AUC
ROC AUC 代表受试者操作者特征——曲线下面积。它是真阳性率对假阳性率曲线下的面积。请记住,真正的正比率也有名称回忆和灵敏度。**
误报率不是我们在本系列中讨论的一个指标。
假阳性率
误报率(FPR) 是一个额外指标。👍它是通过将假阳性除以所有实际阴性来计算的。**
FPR = (FP / N)
假阳性率是我们看到的唯一一个分数越低越好的指标。⬇️=😀
FPR 很少单独使用。这很重要,因为它是进入 ROC AUC 的两个指标之一。
为了可视化 ROC 曲线,可以使用 sklearn 的plot _ ROC _ curve来绘制。函数签名与您在本系列第二篇文章中看到的plot _ precision _ recall _ curve函数相匹配。
plot_roc_curve(estimator, X_test, y_test)
这里有一个 ROC 曲线的例子:
蓝色是模特的表现。橙色是基线
ROC 曲线是一个流行的图,可以帮助您决定在哪里设置决策阈值,以便您可以优化其他指标。
AUC(曲线下面积)的范围可以从 0 . 5 到 1。分数越高越好。得分为 0.5 可不好,在上图中用橙色线表示。☹️
您希望模型的曲线尽可能靠近左上角。你想要高 TPR 和低 FPR。🙂
我们的模型做得不错,但还有改进的余地。😐
ROC AUC 不是您想要手动计算的指标。✍幸运的是,scikit-learn 函数roc _ AUC _ score可以为您完成这项工作。*注意,你需要将预测概率作为第二个参数传递,而不是预测。☝️*
*roc_auc_score(y_test, y_predicted_probabilities)*
当类别相对平衡时,ROC AUC 是一个很好的汇总统计。然而,不平衡的数据可能会误导。关于一个好的讨论,请看这个机器学习掌握帖子。
平衡行为。资料来源:pixabay.com
摘要
在本文中,您了解了平衡准确性、F1 评分和 ROC AUC。
概述
以下是您在本系列中看到的所有评估指标的公式:
- 精度 = (TP + TN) /全部
- 回忆(灵敏度,TPR) = TP / (TP + FN)
- 精度 = TP / (TP + FP)
- 特异性(TNR) = TN / (TN + FP)
- 平衡准确度 = (灵敏度+特异性)/ 2
- F1 得分 = 2 (精度召回)/(精度+召回)
- ROC AUC = *下面积TPR vs FPR***
ROC AUC 代表曲线下的受试者工作特征面积。不代表接收机工作曲线。👍**
结果摘要
以下是夏威夷衬衫示例的结果:
- 准确度= 65%
- 召回率(敏感度,TPR) = 80%
- 精度= 61.5%
- 特异性(TNR) = 50%
- 平衡精度= 65%
- F1 得分= .695
以下是疾病检测示例的结果:
- 准确率= 99%
- 召回率(敏感度,TPR) = 11.1%
- 精度= 33.3%
- 特异性(TNR) = 99.8%
- 平衡精度= 55.5%
- F1 得分= .167
正如我们两个例子的结果所示,对于不平衡的数据,不同的度量描绘了一幅非常不同的画面。
油漆。资料来源:pixabay.com
包装
还有很多很多其他的分类标准,但是掌握这七个应该会让你成为专家!😀
您看到的七个指标是帮助您选择分类模型和这些模型的决策阈值的工具。您的工作是在选择最终模型和设置决策阈值时明智地使用这些指标。
我应该提到另一种评估分类模型的常用方法。您可以为每个假阴性和假阳性的成本附加一个美元值或效用分数。您可以使用这些预期成本来确定使用哪个模型以及在哪里设置决策阈值。
我希望这篇分类标准的介绍对您有所帮助。如果你有,请在你最喜欢的社交媒体上分享,这样其他人也可以找到它。😀
我写关于 Python 、 SQL 、 Docker 和其他技术主题的文章。如果你对此感兴趣,请注册我的数据科学资源邮件列表,点击这里阅读更多信息,帮助你提高技能。👍
选择愉快!😀
Python 的 3 个疯狂的秘密武器
我不知道没有他们我是如何生活的
我写 Python 已经 5 年多了,我的工具集一直在变小,而不是变大。许多工具都是不必要或无用的,其中一些你会简单地放弃。
这三个是我坚持了很长时间的,和其他的不一样,我只是越来越多地使用它们。
秘密武器#1:用 Kite 写得更快,谷歌更少
大多数代码编辑器都有类似如下的自动完成功能:
…它使用语言(有时是库)文档来提供函数名和参数等信息。
当然这没问题,但是如果你的编辑器可以浏览 GitHub 多年的数据并自动完成不仅仅是函数名,而是整行代码呢?
这只是你应该使用 Kite 的三个理由中的第一个。
原因 1:代码行完成
Kite 会查看您的代码库和变量、在线常用的参数名称、文档,然后给出如下超级上下文建议:
从风筝文件
上面的例子显示了 Kite 如何预测你将使用哪些变量,即使它们被命名为一般名称(如b
) 或使用普通名称(如x
或y
)。
…我们花了大约 50 个工程师年的时间对 Github 上的所有代码进行语义索引,构建统计类型推理和丰富的统计模型,以非常深入的方式使用这些语义信息。— 亚当·史密斯,凯特的创始人兼首席执行官
原因 2:文件的副驾驶
如果你从未被告知要做 RTFM,那么你可能没有犯过我犯过的错误。
无论如何,你都应该在烦扰一个高级开发人员或者查看堆栈溢出答案之前阅读文档。
风筝副驾驶使文件愚蠢-容易。它与你的编辑器一起运行,并实时显示你用光标高亮显示的任何对象/功能等的文档。
亲爱的第一份工作的高级开发人员:对不起。现在我真的没有借口不首先在文件中寻找答案。😉
原因 3:在本地私人经营
最重要的是,它的构建为在本地运行,因此你可以非常快速地获得推荐,它离线工作,你的代码永远不会被发送到云端。
这对于互联网连接不好的人和在封闭源代码库中工作的人来说非常重要。
结果
我已经使用 Kite 很多年了,它越来越好了。拥有超过 1700 万美元的投资,这家公司哪儿也不去,而这个工具,出于某种愚蠢的原因,*i̶s̶̶c̶o̶m̶p̶l̶e̶t̶e̶l̶y̶̶f̶r̶e̶e̶.编辑:我提到的第一个功能现在是 Kite Pro 的一部分,其他的仍然是免费的。有一个很棒的 ROI 计算器 用于 说服你的老板 给你买。*
你所要做的就是为你的编辑器下载 Kite 插件,或者下载 copilot,它可以为你安装插件。去得到它!
秘密武器#2:用 MyPy 和静态类型稳定你的代码
Python 是动态类型的,一个过于简单的解释是,你可以在任何时候让任何变量成为任何数据类型(字符串、整数等)。
*# These two variable types are declared the exact same way
# Python figures out the data type on it's own, dynamically# string
var_name = "string here"# integer
var_name = 1234*
相反的是静态类型的语言,其中变量必须有一个特定的数据类型,并且总是遵守它。
*# Many languages require the data type to be declared too# string
str var_name = "string here"# integer
int var_name = 1234*
动态类型的利与弊
动态类型的优点是你可以在写作时偷懒,这样可以减少代码混乱。
尽管缺点很多,也很大:
- 在开发周期的后期,你通常会遇到错误
- 代码性能更差,因为 Python 不断地计算类型
- 函数不太稳定,因为它们的输入和输出可以在没有警告的情况下改变数据类型
- 交付代码更加不稳定,因为其他人可能不知道您的变量是什么数据类型或者可能成为什么数据类型
Python 中的静态类型
从 Python 3.5 开始,您可以本机使用静态类型了!此外,mypy 是一个模块,可以让您快速轻松地扫描您的代码库,查找静态类型违规。
这只是一个如何使用它的例子:
*# Declaring a function using normal dynamic typing, without mypy
def iter_primes():
# code here that returns an iterator of ints# Declaring the same function with static typing
from typing import Iteratordef iter_primes() -> Iterator[int]:
# code here that returns an iterator of ints*
现在你可以用pip install mypy
安装 mypy 进行扫描,用mypy [path to python file]
命令扫描你的代码。这将通知您任何数据类型与您指定的静态类型不匹配的地方。
对于静态示例,我们指定函数返回整数的迭代器。这一简单的更改通过强制执行一致的输出,使函数更加经得起未来的考验。
其他开发人员只需查看声明就可以知道输出的数据类型,而且不像只使用文档,如果不遵守声明,您的代码就会出错。
这是一个取自的超级简单的例子,这里的例子,如果仍然没有意义,去检查一下。
结果
很难列出静态类型可以让你避免未来痛苦的所有方式,但是 mypy docs】有一个很棒的 FAQ ,里面有更多的优点和缺点。
如果你在一个稳定为王的生产代码库中工作,绝对试试 mypy 和类型模块。
秘密武器#3:用 Sonarlint 更快地发现错误并编写更简单的函数
现在每个编辑器都有某种内置的错误检查或“过磅”功能。它查看代码,通常不运行代码,并试图猜测可能出错的地方。这叫做静态代码分析。
VS 代码中的默认 Python Linter
**动态代码分析实际上试图运行/编译你的部分代码,看看它是否正常工作,但它是在后台自动完成的。代替猜测,它实际上知道它是否能工作,以及确切的错误是什么。
SonarLint 是最好的动态代码分析,还有更多优点。这些特点是我喜欢它的原因:
注释或未调用的代码
我犯了留下打印语句、注释掉的代码和未使用的函数遍布我的代码库的错误。这样会警告我,让我很难忘记,告诉我在哪里,很容易找到。
安全风险
一个包含不断更新的安全风险的大型数据库会实时出现在你的代码库中,警告你可能暴露的任何已知漏洞。
安全风险非常小众,不可能背下来,所以大家要用的东西来追踪这些。SonarLint 是一个很好的起点。
从未执行的代码
与未调用的代码略有不同,如果我创建了任何结果不可能达到的评估,这将警告我。这些很难发现,可能会导致几个小时的调试,所以这是我最喜欢的警告之一。
这里有一个例子:
*a = Noneif a == None or not a or a:
this_will_always_get_called()
else:
# sonarlint will warn you about this line never being executed
this_will_never_get_called()*
认知复杂性
这是一个有趣的话题,我可以写一整篇文章,事实上,有一个关于它的整篇白皮书。
简单的解释是,他们创造了一个数学公式,可以评估代码阅读/理解的难度。
它不仅非常有用,而且很容易理解。每次 SonarLint 要求我“降低认知复杂性”时,它都会附带一个对我打破的规则的简单解释,比如“太多嵌套的 if 语句”。
结果
我发现这比基本的阻塞和林挺实践更有用,并且我确信它让我写出了对人类更友好的代码。顺便问一下哪是蟒!
SonarLint 是免费的,所以现在没有理由不去获取它并把它附在你的编辑器上。
结论
如果您跳过了这里,只是一个快速的警告,除非您对这些功能有基本的了解,否则您可能无法正确使用它们。
以下是三种秘密武器的概述:
- 用 Kite Copilot 和编辑器插件写得更快,谷歌更少
- 用 Mypy 和静态类型稳定你的代码
- 使用 SonarLint 编辑器插件更快地发现错误并编写更简单的函数
我希望这些工具能很好地为您服务,我自己也非常喜欢它们。我确信我已经错过了一些其他难以置信的资源,所以一定要在评论中分享你不能没有的东西。
如果你从事数据科学工作,你可能会从我目前的热门文章中发现价值:
这些错误非常普遍,而且非常容易纠正。
towardsdatascience.com](/please-stop-doing-these-5-things-in-pandas-9fbabea897aa)*
计算随机森林中特征重要性的三种方法
特征重要性描述了哪些特征是相关的。它可以帮助更好地理解所解决的问题,有时还可以通过利用特征选择来改进模型。在这篇文章中,我将展示 3 种方法(带代码)来计算来自scikit-learn
包(Python 中)的随机森林算法的特征重要性。
内置随机森林重要性
随机森林算法具有内置的要素重要性,可以通过两种方式计算:
- 基尼系数(或平均减少杂质),由随机森林结构计算得出。让我们看看随机森林是如何构建的。这是一组决策树。每个决策树都是一组内部节点和叶子。在内部节点中,所选择的特征用于决定如何将数据集分成两个独立的集合,其中具有相似的响应。内部节点的特征是根据某种标准选择的,对于分类任务,该标准可以是基尼不纯或信息增益,对于回归,该标准是方差减少。我们可以测量每个特征如何减少分裂的杂质(具有最高减少的特征被选择用于内部节点)。对于每个特征,我们可以收集它平均如何减少杂质。森林中所有树的平均值是要素重要性的度量。该方法可在
[scikit-learn](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html#sklearn.ensemble.RandomForestClassifier.feature_importances_)
中实现的随机森林(对于分类器和回归器都适用)。值得一提的是,在这种方法中,我们应该查看计算出的重要性的相对值。这种方法最大的优点是计算速度快——所有需要的值都是在随机森林训练中计算出来的。该方法的缺点是倾向于选择(选择为重要的)高基数的数字特征和分类特征。此外,在相关特征的情况下,它可以选择其中一个特征,而忽略第二个特征的重要性(这可能导致错误的结论)。 - 平均降低准确度 —是一种基于准确度平均降低来计算置换出袋(OOB)样本特征重要性的方法。这个方法没有在
scikit-learn
包中实现。与这种方法非常相似的是本文下面描述的基于排列的重要性。
我将展示如何使用scikit-learn
包和波士顿数据集计算随机森林的特征重要性(房价回归任务)。
*# Let's load the packages*
**import** **numpy** **as** np
**import** **pandas** **as** pd
**from** **sklearn.datasets** **import** load_boston
**from** **sklearn.model_selection** **import** train_test_split
**from** **sklearn.ensemble** **import** RandomForestRegressor
**from** **sklearn.inspection** **import** permutation_importance
**import** **shap**
**from** **matplotlib** **import** pyplot **as** plt
plt.rcParams.update({'figure.figsize': (12.0, 8.0)})
plt.rcParams.update({'font.size': 14})
加载数据集并拆分用于训练和测试。
boston = load_boston()
X = pd.DataFrame(boston.data, columns=boston.feature_names)
y = boston.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=12)
用 100 棵决策树装配随机森林回归器;
rf = RandomForestRegressor(n_estimators=100)
rf.fit(X_train, y_train)
要从随机森林模型中获得特征重要性,请使用feature_importances_
参数:
rf.feature_importances_array([0.04054781, 0.00149293, 0.00576977, 0.00071805, 0.02944643,
0.25261155, 0.01969354, 0.05781783, 0.0050257 , 0.01615872,
0.01066154, 0.01185997, 0.54819617])
让我们画出重要性(图表比数值更容易理解)。
plt.barh(boston.feature_names, rf.feature_importances_)
为了有一个更好的图表,让我们对特征进行排序,并再次绘图:
sorted_idx = rf.feature_importances_.argsort()
plt.barh(boston.feature_names[sorted_idx], rf.feature_importances_[sorted_idx])
plt.xlabel("Random Forest Feature Importance")
基于排列的重要性
基于排列的重要性可用于克服用平均杂质减少计算的默认特征重要性的缺点。它在scikit-learn
中作为[permutation_importance](https://scikit-learn.org/stable/modules/generated/sklearn.inspection.permutation_importance.html#sklearn.inspection.permutation_importance)
方法实现。作为参数,它需要经过训练的模型(可以是与scikit-learn
API 兼容的任何模型)和验证(测试数据)。这种方法将随机改变每个特征,并计算模型性能的变化。对性能影响最大的特性是最重要的特性。
置换重要性可以很容易地计算出来:
perm_importance = permutation_importance(rf, X_test, y_test)
绘制重要性图:
sorted_idx = perm_importance.importances_mean.argsort()
plt.barh(boston.feature_names[sorted_idx], perm_importance.importances_mean[sorted_idx])
plt.xlabel("Permutation Importance")
基于置换的重要性在计算上是昂贵的。基于排列的方法对于高度相关的特征可能有问题,它可以将它们报告为不重要。
根据 SHAP 值计算重要性
可以使用 SHAP 解释(它是模型不可知的)来计算随机森林的特征重要性。它使用博弈论中的 Shapley 值来估计每个特征对预测的贡献。它可以很容易地安装(pip install shap
)和使用scikit-learn
随机森林:
explainer = shap.TreeExplainer(rf)
shap_values = explainer.shap_values(X_test)
要将特征重要性绘制成水平条形图,我们需要使用summary_plot
方法:
shap.summary_plot(shap_values, X_test, plot_type="bar")
特征重要性可以用更多细节来绘制,显示特征值:
shap.summary_plot(shap_values, X_test)
使用 SHAP 计算特征重要性的计算开销可能很大。然而,它可以提供更多的信息,如决策图或依赖图。
摘要
给出了计算scikit-learn
随机森林的特征重要性的 3 种方法:
- 内置功能重要性
- 基于排列的重要性
- 用 SHAP 值计算
在我看来,检查所有方法并比较结果总是好的。我在 MLJAR 的 AutoML 开源包[mljar-supervised](https://github.com/mljar/mljar-supervised)
中使用了基于排列和 SHAP 的方法。我使用它们是因为它们是模型不可知的,并且可以很好地与非来自scikit-learn
的算法一起工作:Xgboost,神经网络(keras+tensorflow),LigthGBM,CatBoost。
重要注意事项
- 模型越精确,计算出的重要性就越可信。
- 计算的重要性描述了特征对于机器学习模型有多重要。这是数据中要素重要性的近似值
由 mljar 监管的是一个开源的自动机器学习(AutoML) Python 包,可以处理表格数据。它旨在为数据科学家节省时间。它抽象了预处理数据、构建机器学习模型和执行超参数调整以找到最佳模型的通用方法。这不是一个黑匣子,因为你可以确切地看到 ML 管道是如何构建的(每个 ML 模型都有一个详细的降价报告)。
使用 mljar 监督的 AutoML 包生成的示例报告。
3D 宇宙:数据科学、开发和发展
数据科学,DevOps
DevOps 成为数据科学领域独角兽的重要性。
使用 Canva 制作
数据科学不仅仅是数据收集、分析和建模。让模型可部署并便于与 DevOps 工程师沟通也是数据科学家的一项关键技能。作为 DevOps 实践者的数据科学家使他/她成为独角兽。
数据科学更多的是分析数据,而不是程序开发。为了开发模型或程序,我们使用统计学和机器学习从我们的数据中获得洞察力。另一方面,DevOps 基于软件工程和系统工程。它融合了许多敏捷软件方法论的思想。当我们将这些技术应用于数据科学时,它有助于我们记住数据科学与应用程序开发的相似之处和不同之处。
使用 Canva 设计
在数据科学领域,DevOps 的关键领域包括我们将如何创建模型,如何进行传递,如何以受保护的方式维护我们的模型,以及如何根据感兴趣的量来放大/缩小模型。本文将讨论如何从一个想法到生产,再到管理,开始开发一个机器学习模型。我们开始吧!
数据科学过程
数据科学的第一阶段从收集数据和处理数据开始,即分析数据、探索和执行深入分析,以及交流结果。
数据分析(使用 Canva)
- 可以通过构建脚本、使用 API 调用 Web 抓取以及通过许多数据源来收集数据——UCI 机器学习资源库、Kaggle 网站。此外,数据可以来自以日志文件形式存储的任何应用程序。我们使用电子表格、通常用于 web 和分析应用程序的 NoSQL 数据库、关系数据库和其他外部来源来组织数据。
- 收集数据后,我们需要处理数据——过滤缺失值、不必要的内容异常、提取并重新格式化以满足模型需求。此外,我们可能从几个站点/资源收集数据,在这种情况下,我们需要合并具有共同属性的数据(在确定相关属性之后)。最后用 R、Python、Spark、Pandas 构建一个数据框架&开始用数据做实验。
- 确定相关的变量并进行特征工程(从原始属性中获得附加特征)以提高模型性能。最后,需要根据数据类型以及需要完成的培训、测试和验证来选择算法。
数据科学模型的版本控制
现在假设一位数据科学家遵循了上述所有要求,并将其代码部署到生产中。它能完美地工作而没有任何问题吗?需求的数量可能会增加,您将无法管理已部署的服务。如果您的最终用户希望应用程序提供更多功能,该怎么办?您会再次开始训练模型、构建模型并进行部署吗?这是一个连续的循环,为什么我们不能将这个过程自动化呢?是的,我们可以通过持续集成来实现这一点。
GitHub 知识库(作者提供图片)
有时我们与涉及一个大团队的应用程序一起工作来开发一个产品,它也需要在一个连续的时期内以位和字节来开发。对于这样的应用程序,版本控制帮助我们跟踪模型的每一个版本,跟踪其他团队成员已经更新的变更,并且与合作者共享代码。GitHub 是一个代码库托管服务,使用 Git 版本控制系统。在这里,我们创建一个存储库,并像在本地机器上一样组织我们的项目目录。每当有一个 bug 修复或增强或添加一个功能时,我们的经理或负责人就会提出一个问题,并指派一些人来处理这个问题。现在,这些人开始解决这个问题,也就是说,在我们的例子中提高模型的性能,或者添加一些应用程序特性。
使用自动化工具测试和部署模型
测试对于确保应用程序不会导致任何失败是非常重要的,因为这可能会在未来或开发的后期花费我们一大笔钱。在数据科学的世界里,人们需要进行单元测试来检查代码的功能,然后评估模型的性能。
詹金斯管道(作者照片)
让我们举一个当前情况的实时例子,即 COVID19,假设您正在开发一个应用程序来跟踪您所在位置附近的冠状动脉患者。您已经做了所有的事情,训练并测试了模型,还验证了算法以确保它们和预期的一样准确。但是,这是否足以毫无困难地运行应用程序呢?不,因为这里的数据是动态变化的——(数据漂移),因为我们每天在给定的地区可能没有相同数量的阳性病例。对此,什么是理想的解决方案?让生成的数据集遵循相同的预处理步骤,开发和部署模型。
这个繁琐的过程可以通过在 Jenkins(一个 CI 工具)上创建一个带有定义好的管道的作业来避免,如上图所示,所以您将在 Jenkins 上为模型创建一个管道,并要求它在您的数据集使用 API 调用更新时构建、测试、验证和部署。Jenkins 作业也可以定期触发,手动触发,或者当有人将更改推送到 GitHub 存储库时触发。
一些部署实践
人们可以遵循某些部署实践来以环境的形式管理整个过程。当我们在 DevOps 中谈论环境时,它意味着需要运行应用程序或服务的每个部分的基础设施的集合。
- 部署到试运行 —试运行环境是我们的代码准备好被部署的地方,但是我们用它来执行质量保证和集成测试。这是我们的模型首次测试各种功能的地方,就像一个在线电子商务平台,它根据用户以前的订单和搜索结果推荐他们的用户。一旦代码被部署到一个阶段环境中,我们就可以随时发布新的应用程序版本。在此之前,我们可以将工件存储在 Artifactory 上,或者如果我们使用 docker,docker 文件可以存储在 DockerHub 上。
- 金丝雀部署— 当新版本发布时,我们的客户可能会在一段时间内无法使用该服务,因为我们正在更新他们。停机可能会严重影响业务运营。更新时的小错误会对服务造成不良影响。为了避免这种情况,我们使用金丝雀部署。例如,如果我们有 4 台服务器在运行,我们将只使用新功能更新服务器 1,并且只将 10%的流量路由到该服务器,所有其余的服务器将获得 30%的流量。这样,即使服务器 1 出现故障,客户也可以使用其余 3 台服务器。在成功运行新更新的服务器 1 之后,我们移动下一个服务器,并重复该过程。在这里,我们只对某些用户进行更改,即只对这些服务器部署模型或更新更改。
蓝绿色部署(使用 Canva 设计)
蓝绿部署涉及 2 个系统,一个在线,一个离线。我们升级离线系统,对其进行测试&一旦测试成功,就将流量路由到离线系统。如果有任何问题,我们将返回。
- 混沌猴子— 这将在实时过程中破坏或杀死你的服务器,迫使你的工程师找到使服务器健壮的方法&容忍即时故障。网飞使用混沌猴子。
保护和监控生产中的模型
保护模型(使用画布制作)
数据科学模型需要像任何其他生产应用程序一样得到保护和保护。但是对于数据科学来说,安全性通常不被认为是一个重要因素。部署模型文件时,需要一定级别的加密。应用程序将以可执行的格式存储。保护模型的另一种方法是使用访问控制,这涉及两种机制——身份验证,我们确认使用我们服务的人的身份。授权,一组经过身份验证的用户可以执行的操作,例如,我们团队中的所有用户都不允许写入数据库。并且只有一些用户有权限执行该模型。
运营安全通过分配角色、变更管理、日志监控和审核来处理职责分离。需要备份数据,设计高可用性,为测试故障留出时间,为灾难恢复制定业务连续性计划。
GCP 度量探索者(作者提供照片)
一旦模型投入生产,我们可能会遇到某些问题,事件可能会发生,如果不立即解决,会导致巨大的损失。DevOps 是关于开发系统并保持它们运行的。绩效监控是实现这一目标的重要实践。它包括跟踪资源利用率(CPU、磁盘),确保服务对我们的用户可用,测量吞吐量和运行效率等。
有几个 GUI 工具用于监控,上图显示了在 Google 云平台上创建的一个实例的 CPU 利用率。像 Nagios、Google StackDriver、AWS CloudWatch、New Relic、聚合器的 PagerDuty 等工具。在将模型部署到产品中之后,我们需要确保测量模型的质量。进行一些样本预测,评估准确性,如果准确性由于模型漂移而失败,则使用新数据重新训练模型。
集装箱化
容器是运行图像的实例,它们有自己的环境。容器包括代码、变更、流程、网络、依赖关系和操作系统。它们就像通过共享同一个内核来完成特定任务的独立实例。
Docker 是一个在容器中运行应用程序的平台。
使用 Canva 制作
但是我们为什么需要容器呢?对于数据科学家来说,一个主要问题是我们使用的各种工具或软件包的兼容性问题。
**ERROR: Could not find a version that satisfies the requirement install.**
您可能已经多次看到此错误,这是因为您在以前的项目中使用的工具可能会更新,或者更新一个包/工具会反映其他几个工具。使用 docker,我们可以在一个容器中运行一个组件,它的依赖项和库都在同一个虚拟机和操作系统上。例如,在一个容器上运行您的模型的 flask 服务器可以与在另一个容器上运行的数据库服务器通信。
Dockerfile 文件
这是一个带有配置映像的文本文件,包括基本映像、软件包、网络端口和启动命令。通过使用这个文件,我们构建 docker 图像(创建容器的模板)。任何拥有 docker 映像的人都可以运行该容器。下面是一个运行 flask 服务器的简单 docker 文件。现在,通过将该文件包含在我们的 GitHub 存储库中,人们可以通过使用各种云提供商轻松地将该模型投入生产。但是记住我们不想犯任何错误。
FROM python:3.7-slimWORKDIR /appADD . /appRUN pip install --trusted-host pypi.python.org -r requirements.txtEXPOSE 5000ENV NAME OpentoAllCMD ["python","app.py"]
假设您开发了一个 web 应用程序来预订新年晚会的门票。与 12 月 20 日相比,服务器请求从 12 月 30 日开始快速增加。你听说过网飞的服务器瘫痪了吗?是的,最近由于一部著名的西班牙电视剧。那么我们是如何遇到这样的问题的呢?在理想情况下,我们需要根据容量需求增加新的服务器,但在这种情况下,我们可能会因人为错误而导致巨大的失败。顾客会给出负面反馈,他们可能会选择其他平台观看其他节目,或者他们可能会选择其他网站订票。
这可以通过类似 Kubernetes、Amazon ECS、Docker Swarm、Google Kubernetes Engine、Titus(网飞使用的)等容器管理工具来避免。这些流程编排工具支持创建和执行可预测、可重复、可自动化的流程。此外,它们还提供自动扩展功能,根据网络流量,新服务器将加速运行,如果流量减少,它们将缩小服务器的规模,这样每个人都可以在不停机的情况下访问服务。
结论
数据科学是一种构建模型的实践,模型是一种代码或软件,DevOps 可以减轻他们的负担,正如本文所述。DevOps 工程师不太了解数据科学家,数据科学家以及他们与 DevOps 的关系也是如此。我写这篇文章的目的是传达他们各自的需求和期望,这样他们就可以一起工作来构建无错误的应用程序。
如果你发现有改进的地方,请随时提出修改建议。我很快会再赶上你的。在那之前,呆在家里,注意安全!
如果您想联系,在 LinkedIn 上联系我。
2020 年数据科学的 4 个最热门趋势
2019 年对于所有的数据科学来说是重要的一年。
想获得灵感?快来加入我的 超级行情快讯 。😎
全球各行各业的公司都在经历人们所说的 数字化转型 。也就是说,企业正在采取传统的业务流程,如招聘、营销、定价和战略,并使用数字技术使它们好 10 倍。
数据科学已经成为这些变革不可或缺的一部分。有了数据科学,组织不再需要根据直觉、最佳猜测或小型调查来做出重要决策。相反,他们正在分析大量的真实数据,以便根据真实的数据驱动的事实做出决策。这才是数据科学真正的意义所在——通过数据创造价值。
这种将数据整合到核心业务流程中的趋势已经显著增长,根据谷歌搜索趋势的数据,在过去 5 年中,人们的兴趣增加了 4 倍多。数据给公司带来了超越竞争对手的巨大优势。随着更多的数据和更好的数据科学家使用这些数据,公司可以获得有关市场的信息,而这些信息可能连他们的竞争对手都不知道。这已经成为一个数据或灭亡的游戏。
谷歌搜索过去 5 年“数据科学”的受欢迎程度。由谷歌趋势生成。
在当今不断发展的数字世界中,保持竞争优势需要不断创新。专利已经过时,而敏捷方法和快速捕捉新趋势非常流行。
组织不能再依赖他们过去坚如磐石的方法。如果像数据科学、人工智能或区块链这样的新趋势出现,需要提前预期并快速适应。
以下是 2020 年最热门的 4 大数据科学趋势。这些趋势今年引起了越来越多的关注,并将在 2020 年继续增长。
(1)自动化数据科学
即使在今天的数字时代,数据科学仍然需要大量的手工工作。存储数据,清洗数据,可视化和探索数据,最后对数据建模,得到一些实际的结果。手工工作只是乞求自动化,因此出现了自动化数据科学和机器学习。
数据科学管道的几乎每一步都已经或正在实现自动化。
在过去的几年里,自动数据清理已经在进行了大量的研究。清理大数据经常占用数据科学家的大部分宝贵时间。无论是初创公司还是像 IBM 这样的大公司都提供数据清理的自动化和工具。
数据科学的另一大部分被称为特征工程已经经历了重大的破坏。特征工具为自动特征工程提供解决方案。最重要的是,现代的深度学习技术,如卷积和递归神经网络学习它们自己的特征,而不需要手动的特征设计。
也许最重要的自动化发生在机器学习领域。数据机器人和 H2O 都通过提供端到端的机器学习平台在行业中确立了自己的地位,让数据科学家可以非常轻松地处理数据管理和模型构建。 AutoML ,一种自动模型设计和训练的方法,也在 2019 年蓬勃发展,因为这些自动模型超过了最先进的水平。特别是谷歌,正在大力投资云 AutoML 。
总的来说,公司正在大量投资于构建和购买自动化数据科学的工具和服务。只要能让这个过程更便宜、更简单。同时,这种自动化也迎合了规模较小、技术含量较低的组织,他们可以利用这些工具和服务来访问数据科学,而无需组建自己的团队。
(2)数据隐私和安全
隐私和安全一直是技术领域的敏感话题。所有公司都希望快速行动和创新,但在隐私或安全问题上失去客户的信任可能是致命的。因此,他们被迫把它作为一个优先事项,至少不泄露私人数据的最低限度。
在过去的一年里,数据隐私和安全已经成为一个令人难以置信的热门话题,因为这些问题被大量的公共黑客所放大。就在最近的 2019 年 11 月 22 日,在 Google Cloud 上发现了一个暴露的没有安全性的服务器。该服务器包含了 12 亿人的个人信息,包括姓名、电子邮件地址、电话号码、LinkedIn 和脸书的个人资料。甚至联邦调查局也介入调查。这是有史以来最大的数据暴露之一。
数据是怎么来的?它属于谁?谁负责这些数据的安全?它在谷歌云服务器上,实际上任何人都可以创建它。
现在我们可以放心了,全世界都不会在看完新闻后关闭他们的 LinkedIn 和脸书账户,但这确实令人惊讶。消费者变得越来越小心他们给谁他们的电子邮件地址和电话号码。
一家能够保证客户数据隐私和安全的公司会发现,他们更容易说服客户提供更多数据(通过继续使用他们的产品和服务)。它还确保,如果他们的政府颁布任何法律要求客户数据的安全协议,他们已经做好了充分的准备。许多公司选择 SOC 2 合规性来证明他们的安全强度。
整个数据科学过程都是由数据推动的,但大多数数据都不是匿名的。如果落入坏人之手,这些数据可能会被用来加剧全球灾难,扰乱人们的日常隐私和生计。数据不仅仅是原始数字,它代表并描述了真实的人和真实的事物。
随着数据科学的发展,我们也将看到围绕数据的隐私和安全协议的转变。这包括建立和维护数据的安全性、可靠性和完整性的流程、法律和不同方法。如果网络安全成为今年的新流行语,那就不足为奇了。
(3)云中的超大规模数据科学
多年来,数据科学已经从一个小众领域发展成为一个完整的领域,可供分析的数据也在规模上爆炸式增长。组织正在收集和存储比以往更多的数据。
一家典型的财富 500 强公司可能需要分析的数据量已经远远超出了个人电脑的处理能力。一台像样的电脑可能有 64GB 的内存,8 核 CPU 和 4TB 的存储空间。这对于个人项目来说很好,但当你为一家全球性公司工作时就不那么好了,比如拥有覆盖数百万客户数据的银行或零售商。
这就是云计算进入该领域的原因。云计算为任何地方的任何人提供了几乎无限的处理能力。亚马逊网络服务(AWS)等云供应商提供多达 96 个虚拟 CPU 核心和高达 768 GB 内存的服务器。这些服务器可以设置在一个自动扩展组中,其中数百台服务器可以在没有太多延迟的情况下启动或停止,即按需提供计算能力。
谷歌云数据中心
除了计算,云计算公司还提供成熟的数据分析平台。谷歌云提供了一个名为 BigQuery 的平台,这是一个无服务器和可扩展的数据仓库,让数据科学家能够在单个平台上存储和分析数 Pb 的数据。BigQuery 还可以连接到数据科学的其他 GCP 服务。使用云数据流创建数据流管道,云数据处理器在数据上运行 Hadoop 或 Apache Spark,或者使用 BigQuery ML 在庞大的数据集上建立机器学习模型。
从数据到处理能力的一切都在增长。随着数据科学的成熟,由于数据量巨大,我们最终可能会完全在云上进行数据科学。
(4)自然语言处理
自然语言处理 (NLP)在深度学习研究取得巨大突破后,已经坚定地进入数据科学。
数据科学最初是从分析纯粹的原始数据开始的,因为这是处理数据和在电子表格中收集数据的最简单方法。如果您需要处理任何类型的文本,通常需要对其进行分类或以某种方式转换成数字。
然而,将一段文字压缩成一个数字是很有挑战性的。自然语言和文本包含如此丰富的数据和信息——我们过去常常会错过这些信息,因为我们缺乏用数字表示这些信息的能力。
通过深度学习在 NLP 方面取得的巨大进步正在推动 NLP 全面集成到我们的常规数据分析中。神经网络现在可以以令人难以置信的速度从大量文本中提取信息。他们能够将文本分为不同的类别,确定对文本的情感,并对文本数据的相似性进行分析。最终,所有这些信息都可以存储在一个单一的数字特征向量中。
因此,NLP 成为数据科学中的一个强有力的工具。庞大的文本数据库,不仅仅是一个单词的答案,而是完整的段落,可以转换成数字数据进行标准分析。我们现在能够探索更加复杂的数据集。
例如,假设一个新闻网站想要查看哪些主题获得了更多的浏览量。如果没有先进的自然语言处理技术,人们所能做的就是输入关键词,或者仅仅是凭直觉判断为什么一个特定的标题比另一个好。有了今天的自然语言处理,我们就能够量化网站上的文本,比较整段文本甚至网页,从而获得更全面的见解。
对于过去几年中 NLP 最重要的进步的技术概述,你可以查看由 Victor Sanh 撰写的指南。
数据科学作为一个整体正在发展。随着其能力的增长,它正在嵌入每个行业,包括技术性和非技术性行业,以及每个企业,包括小型和大型企业。
随着该领域的长期发展,看到它大规模地大众化,成为我们软件工具箱中的一个工具供更多的人使用,就不足为奇了。
新冠肺炎危机教给世界的关于数据的 4 个教训
意见
新冠肺炎危机至少有一个积极的结果:它让许多人了解了数据解读的困难。
新冠肺炎全球危机对地球上大多数人的日常生活产生了强烈影响。它还创造了一个前所未有的时间,数亿人开始每天分析图表、数据点和流行病学指标。
无论情况有多糟糕,总会有好的结果。除了团结的伟大表现,新冠肺炎危机教会了许多人如何看待和解释数据。通常,他们意识到事情并不像看起来那么简单。
数据收集并不简单
从局外人的角度来看,收集数据似乎是一项简单的任务。既然数据库已经被使用了几十年,那么必须有一个像发送电子邮件来更新中央数据库一样简单的系统。我们都希望。
实际上,这并不容易。有许多数据库系统,许多存储数据的方法,许多对信息进行编码的方法,也有许多对价值进行分类的方法。当你想创建一个中央数据库来收集所有数据时,比如说,新冠肺炎病例和死亡数据,你必须确保所有与你合作的实体使用相同的工具、软件版本、定义、类别名称、文件格式、编码等等
由于疫情极其罕见的性质,大多数国家或州没有类似的系统,必须在几周内临时准备一些东西,通常依靠员工尽力而为,但他们绝不是数据仓库架构师。
通常,最快捷、最自然的解决方案似乎是 Excel。它被广泛使用,并被视为一种标准格式。然后,不同的地区、州、医院或疗养院将他们的日常数据以 Excel 文件的形式(当你知道其他人发送了 pdf 文件时,这仍然是可以的)发送到一个中央实体,该实体的不幸任务是将所有这些数据汇编在一起。
你收到消息了。[ 通过这个发电机
Excel 对于许多任务来说是一个很好的工具,但是它不是一个数据库。它故意弄乱数据类型(这是一个如此普遍的问题,以至于一些人类基因不得不因为 Excel 而被重命名)或者它能够处理的条目数量有限。最近提出使用过时版本的 Excel 无法处理超过 65,536 行,这是英国超过 16,000 例新冠肺炎病例没有被通知和考虑的原因。使用不适当的软件和数据收集实践确实对公共卫生产生了重大影响。
定义很重要
现在让我们假设我们收集的数据是正确的,这只是这条路的开始。获得正确的定义是另一项任务,起初看起来很琐碎,但实际上并不是这样。例如,让我们从这个简单的问题开始:
什么算新冠肺炎死亡?
看起来很简单,对吗?类似于“被病毒感染后死亡的人”听起来是一个合适的答案。实际上,事情并不顺利。如果我们看下面的图表,我们可以看到 2020 年美国的超额死亡人数,大概是由新冠肺炎造成的。实际上,大约三分之一的超额死亡不是新冠肺炎造成的。
美国归因和非归因的超额死亡。图摘自温伯格等人的文章[ 链接 ]
这些死亡中的大多数很可能与新冠肺炎有关(尽管可能是间接的),否则,我们将面临另一个未确定的过度死亡原因的重大问题。
这种差异的原因是多种多样的,取决于国家或州。在某些情况下,疗养院或医院外的死亡数没有统计或仅在几天后才公布。非肺炎症状导致的死亡可能没有被报道。此外,社会隔离有时会因为缺少亲近的证人而难以确定死因。这还不包括那些可能倾向于少报死亡人数的政客。
这个例子告诉我们,得到正确的简单定义并不容易,需要比预期更多的计划和验证。此外,从不同的角度看数据(例如,不仅看报告的 Covd-19 死亡,也看全球死亡),有助于更清楚地了解现实。
选择正确的指标并不简单
获得正确的定义仍然不是最终目标。你还必须确保你正在寻找正确的衡量标准来评估情况并做出正确的决定。
在第一波疫情期间,人们开始密切跟踪多个指标,以更好地把握形势的严重性。一些研究着眼于活跃或累积病例的数量,死亡人数,或者在更罕见的情况下,重症监护病房(ICU)的入住率。
所有这些指标显示了疫情的不同方面,但它们都依赖于不同的假设和方法偏差,例如:
- 我们如何统计死亡人数?(见上文)
- 什么是阳性病例?(仅症状?需要测试结果吗?如果有,是哪个测试?)
为了更好地理解如何解释指标,我们必须了解它是如何产生的。以下示例显示了西班牙新冠肺炎病例(红色)和死亡病例(黑色)的演变。如果你不看红色曲线的背景,你会认为现在的情况比 3 月和 4 月时更糟糕,当时西班牙已经是受影响最严重的国家之一。
西班牙累计确诊新冠肺炎病例数。[ 来源
但细看更多细节,现实就不一样了。
首先,我们可以看到,大多数死亡是在早期积累的,在第二波传染病期间,死亡人数要少得多(但仍有一些人死亡)。
西班牙累计新冠肺炎死亡人数。形状与累计病例数大相径庭(见上)。[ 来源
第二,曲线没有解释检测方法是如何演变的。在疫情开始时,用于检测病例的资源很少,流程也没有正确定义。现在,PCR 测试(最敏感的测试)每天都在大量进行。即使是无症状的人也要接受检测(在第二波中,西班牙约有 40%的阳性病例),而 3 月和 4 月的情况并非如此。
这可能是这次疫情的主要收获之一。许多人现在不会盲目地看一个指标,而是会质疑它是如何产生的,并且会反射性地看不同的指标来了解当前的情况。
取样也很重要
新冠肺炎疫情产生了大量任何人都可以访问、解读或辩论的数据。然后,试图比较所有这些数据点,让我们自己了解情况,谁知道呢,会比专家更聪明。
但是,在实践中,这些比较往往是棘手的,往往是由于抽样方法的差异。抽样是统计学课堂上教授的第一个概念之一,因为它在我们如何解释数据方面起着核心作用,新冠肺炎的数据向我们展示了许多由于数据生成方式而难以进行比较或分析的例子。
例 1:我们无法比较不同时期的测试数据
随着疫情的进步,各国大规模测试人口的能力也在提高。早在 4 月份,很少有人接受检测,他们大多数人的症状与新冠肺炎病毒相符。最近几周,测试次数比最初增加了 5 到 10 倍。
法国、英国和西班牙每千人每天测试的次数— [ 来源
这意味着测试一个人的标准现在不同了,因为我们可以测试更多的人。虽然我们最初主要测试高度易感的人,但现在常见的是测试那些仅仅因为他们与受感染的患者接触过的人,或者纯粹为了大规模筛查活动而随机测试的人。
这意味着我们不能直接比较数据,如阳性检测的百分比或无症状病例的数量,因为采样程序随着时间的推移已经发生了巨大的变化。随着时间的推移有不同的值是正常的,因为 1)病毒的流行不断演变,2)我们根据不同的标准对不同的人群进行采样。毕竟,我们的目标不是设计一个简洁的实验过程,而是提供对疫情最好的反应。
例 2:我们不能(直接)比较国家之间的死亡率风险
随着疫情成为全球性的,来自不同国家的数据进行了比较,以更好地了解哪些策略是有效的或无效的。很快就变得很明显,一些指标,如死亡率风险,在不同国家之间有很大差异,像西班牙,意大利或瑞典等国家显示出更高的比率。
不同国家死亡率风险的演变— [ 来源
然而,很明显,我们无法从这些数据中得出明确的结论,因为许多参数因国而异,例如:
- 人口年龄结构(例如,意大利是世界上平均人口年龄最高的国家之一,而新冠肺炎老年人的死亡率更高)
- 社会互动水平
- 国内的基因多样性(一些基因变异使人们或多或少容易患病,包括新冠肺炎)
- 医疗基础设施(如 ICU 床位数量)
- 对疫情的反应(例如,意大利和瑞典都有很高的死亡率,但他们采取了非常不同的方法来抗击疫情)
- 数据质量(如前所述,并非所有国家都以相同的方式收集或定义数据)
虽然您可以使用适当的统计模型来校正其中的一些因素(如人口年龄结构),但当涉及不同的数据收集方法或人口行为时,情况会变得更加复杂。
总的来说,人们越来越意识到,在很多情况下“我们就是无法比较”,这可能是一件好事,因为这是处理任何数据时的一项关键技能。
让我们不要忘记
令人欣慰的是,随着情况的好转,我们很快将不再需要担心每天新增的新冠肺炎病例或其他类似的数据。但是,为了更好地理解我们的世界和面对其他挑战,例如气候变化,总会有数据需要解释。然后,让我们不要忘记我们从这场危机中学到了什么,让我们在解读数据时总是问自己:
- 数据是如何获得(样本)和收集的?
- 定义是什么意思?
- 看哪个指标更有意义?
希望这能帮助我们做出更好的决定。
2020 年成为计算机视觉专家的 4 步指南
我在过去 2-3 年的旅程中学到了什么
计算机视觉正在崛起!见鬼,它正以极快的速度扩张!这个领域中不断出现的大量创新使得新手或初学者很难轻易在其中立足。在这篇文章中,我将谈论一些不可或缺的东西,同时对初学者非常有益,最终将帮助他们随着时间的推移在这个领域获得高于平均水平的专业知识。考虑到这些想法和教训来自我自己的经历,从一个初学者爬上同样的台阶,你可以期待他们是务实的。
描述掌握任何技能的邓宁-克鲁格曲线
首先要做的事情!这是如何成为计算机视觉专家的 而不是捷径 。我要说的步骤不是一个人可以在 1 个月内掌握的,为下一次面试做准备。但是这些将帮助你在 4-9 个月的时间里真正进入这个领域,这取决于你的热情。
我们已经走了这么远了!以前没看过的一定要看!
1.理论:
好吧好吧好吧!这应该是不同的,但我在这里开始与相同的旧东西。但说实话,一个人需要学习理论来正确地了解这个主题,并对它有一个整体的想法。没有人告诉你要记住你读到和看到的所有东西,但重要的是你至少要从头到尾看一遍。也许,你能找到的最好的资源就是下面的课程:
- 斯坦福的机器学习 : 这只是在你不习惯基本的机器学习的情况下。即使在深度学习的时代,基本的机器学习仍然非常非常重要。这是由吴恩达开发的课程,开启了 MOOC 革命,至今仍有重要意义。
- deeplearning.ai 的深度学习专门化(Deep Learning Specialization):这是当前人工智能领域最好的(如果不是最好的)资源之一。吴恩达再次涵盖了深度学习的关键和核心主题的整个范围。第四门课程涵盖了卷积神经网络,但在我看来,除了第五门课程(序列模型)之外,其他课程对一个有抱负的计算机视觉科学家都很重要。请记住,有些人认为这一课程和上述课程有点肤浅,而少数人认为这太理论化了。所以公平地说,它在难度方面取得了公平的平衡。
- 用于视觉识别的卷积神经网络 (CS231n)斯坦福:如果要总结这个列表,这是不能忽略的。由另一位斯坦福大学的研究人员费教授,这门课程比上述课程更难(因为它是为真正的大学生开设的),并且只专注于计算机视觉。虽然我还没有经历过这一点,但它非常有名,在社区中受到好评。多年来,这门课程一直由安德烈·卡帕西和贾斯廷·约翰逊这样的计算机视觉领域的知名人士教授。
- Udacity 的计算机视觉纳米学位:这个订阅费很高,但很值。它涵盖了基础知识,并深入到应用方面,如图像字幕,物体检测和跟踪等。另外,你可以和 PyTorch 一起工作,py torch 在这个领域肯定会超过 Keras-Tensorflow。
- [计算机视觉中的深度学习](http://Deep Learning in Computer Vision)由 HSE 提供:本课程是俄罗斯国立研究大学更大专业的一部分,它从计算机视觉中的深度学习的角度覆盖了很多 SOTA 的研究。HSE 是 Coursera 上评分最高的课程提供商之一。
2.竞赛:
一旦你掌握了所有的基础知识,甚至在你掌握所有的基础知识之前,你必须尝试你所学的东西。为此,竞争意识有时是有益的。在线人工智能平台的世界来了。Kaggle 一直是这一领域的先锋。它彻底改变了机器学习领域,因为它创造了一个研究人员社区,并为初学者开辟了机会之路。这方面其他值得注意的平台有 AICrowd 、 CodaLab 、 DrivenData 等。甚至脸书研究公司现在也在举办公开比赛。MLContests.com汇总列出了很多不同平台正在进行的比赛。
这些平台通常在一年中的任何时候都有大量的计算机视觉比赛在进行。其中一些是 CVPR,ICCV 研讨会的挑战,而其他的是由公司赞助的。对新人来说,比排名的诱惑力更大的是公共笔记本和该领域专家讨论的结合。这些打开了一个巨大的新的信息和技能集,不能从理论和课程中学到。
Kaggle 竞赛仪表板
让我们来看一个例子。我目前正在做“全球小麦检测”的挑战,你可以在我上面的仪表盘中看到。人们正在使用的最有前途的架构是 EfficientDet、FasterRCNN 和 YOLO。我知道最后两个,但不知道 EfficientDet。我有机会熟悉这个强大的模型。各种最新架构和相关代码的笔记本海洋不仅对初学者有益,对经验丰富的从业者也同样有益。
3.个人项目:
如果你想向世界展示你在这个领域高于平均水平,最好建点东西。没有人在这个领域寻找应用程序。事实上,如果你能做到这一点,这意味着你有能力创建和部署基于人工智能的应用程序,这是所有初创公司都在寻找的宝贵技能。抛开这些不谈,创建小规模的项目并不困难。只是不要做其他人都在做的事情,这是常识!不要成为在 GitHub 账户上只有一个储存库的人,那就是泰坦尼克号幸存者。
招聘人员越来越关注你的项目,以评估你的能力。在这种情况下,你在做什么比你做得多好更重要。澄清一下,在混乱的数据集上对一个有趣的问题获得 50%的准确率要比在 MNIST 上获得 99.98%的准确率好得多。
一些可以尝试的事情:
- 为数据集上的任何问题创建一个精心制作的笔记本。更好的方法是在存储库中创建一组笔记本来处理不同的问题,或者对同一数据集使用不同的方法。
- 开发一个你认为有用的小软件包。它不必是开创性的,但只要让事情变得更容易。它会让你对设计和编写人们可以使用的代码有更多的了解。如果你喜欢,可以在 pip 上主持。衣领上多了一颗星。
开始这一切的那个,Yann LeCun 的 LeNet-5
4.研究论文:
为了成为真正的简历专家,她/他需要阅读研究论文。随着这个领域的发展,每天都有新的网络出现。这在很大程度上与学术界的出版或灭亡文化有关,但即使忽略小规模的改进论文,其他杰出论文的列表也足够长了。这肯定不容易,但是养成阅读论文的习惯会让你在更小的领域获得更广泛的知识。
你最需要的,也是能让你从具备上述技能的人中脱颖而出的,是发表你自己的研究。在 CVPR、ICCV、NeurIPS 等顶级会议上发表文章。你的简历肯定会受到很大的不同对待。这些会议通常有很多研讨会。将这样的研讨会作为第一份出版物的目标可能是一个明智的决定。许多计算机视觉职位的招聘信息已经开始将此作为首选,甚至是申请的最低资格。
5.额外见解:
一、访问的复杂性
阻止人们进入计算机视觉领域的一大障碍仍然是获得负担得起的计算能力的问题。让我们诚实地说,除非你在大学或在投资深度学习的公司工作,否则你很可能必须经历很多磨难来训练你的模型或自掏腰包。
这篇 对于那些希望做出最佳经济选择的人来说,是一篇非常有用的文章。如果你在网上寻找简单免费的 GPU 选项,你有 Google Colab 和 Kaggle 笔记本。Colab 允许您运行代码 12 个小时,并提供 GPU/TPU。而 Kaggle 给你 30 小时/周的 GPU 和 TPU 运行时间。至于付费提供商,我发现对于业余爱好者来说,vast.ai 是一个非常好的选择。
二。乔布斯
最后,如果你想问什么是让你在当前工作中有别于其他人的关键因素,我会说是 3 件事,不分先后:
- 出版物、GitHub 个人资料和 Kaggle 个人资料。
这三个人越来越多地被招聘者所关注,而以前却不是这样。这是无视你的正规教育。大多数顶级职位的最低要求是博士学位。而其他公司的要求可能没有这么严格。
祝你一路顺风!请记住…
“一夜成名需要 20 年。”
拟合机器学习模型前的 4 个必要步骤
一种简单的面向对象的数据处理方法。
照片由 chuttersnap 在 Unsplash 上拍摄
Learning Rate 是为那些对 AI 和 MLOps 的世界感到好奇的人准备的时事通讯。你会在每周五收到我关于最新人工智能新闻和文章的更新和想法。订阅这里!
在一个通用的机器学习管道中有许多步骤,并且在设计它的时候有很多想法。有问题定义、数据采集、错误检测和数据清理等。在这个故事中,我们首先假设我们有一个干净的现成数据集。
考虑到这一点,我们概述了在拟合任何机器学习模型之前的四个必要步骤。然后,我们在 Pytorch 中实现这些步骤,使用公共语法调用多个方法调用;方法链接。我们的目标是定义一个简单而通用的 API,将任何原始数据集转换成机器学习模型可以使用的格式。
为此,我们将使用 构建模式 、,它使用逐步方法构建一个复杂的对象。
builder 模式是一种设计模式,为面向对象编程中的对象创建问题提供了一种灵活的解决方案。它的目的是把一个复杂物体的构造和它的表现分离开来。
那么,这四样东西是什么?在最简单的情况下,建模前的数据处理包括四个不同的动作:
- 加载数据
- 分成训练/有效/测试集
- 标记数据元组
- 获取批量数据
在接下来的小节中,我将逐一分析这四个步骤,并用代码实现它们。我们的目标是最终创建一个 PyTorch DataLoader
,PyTorch 使用这个抽象来表示数据集上的 iterable。拥有一个DataLoader
是设置训练循环的第一步。所以,事不宜迟,让我们把手弄脏吧。
加载数据
对于这个例子,我们使用一个模拟数据集,它以 pandas DataFrame
格式保存。我们的目标是为训练集和验证集分别创建一个 PyTorch Dataloader
类。因此,让我们构建一个名为DataLoaderBuilder
的类,它负责构建这些类。
我们看到DataLoaderBuilder
的唯一操作是存储一个data
变量,它的类型是torch.tensor
。所以现在,我们需要一个从熊猫DataFrame
初始化它的方法。为此,我们使用 python classmethod
。
classmethod
是一个普通的 python 类方法,但是它没有接收self
作为第一个参数,而是接收了一个class
。因此,给定一个熊猫DataFrame
,我们将DataFrame
转换成 PyTorch 张量并实例化DataLoaderBuilder
类,该类作为cls
参数传递给方法。可选地,我们可以只保留我们关心的DataFrame
的列。在定义它之后,我们将它修补到主DataLoaderBuilder
类。
分为培训和验证
对于这个例子,我们将数据集分成两组;培训和验证。很容易扩展代码,把它分成三组;培训、验证和测试。
我们希望随机分割数据集,保留一定比例的数据用于训练,并留出一些数据用于验证。为此,我们使用 Pytorch 的SubsetRandomSampler
。你可以在 PyTorch 的官方文档中读到更多关于这个采样器和更多采样方法的信息。
默认情况下,我们保留 90%的数据用于训练,并且我们跨行拆分(axis=0
)。代码中的另一个细节是我们返回 **self**
。因此,在创建了 **train_data**
和**valid_data**
split 之后,我们返回了整个类。这将允许 as 最终使用方法链接。
标注数据集
接下来,我们应该标记数据集。很多时候,我们用一些特征变量来预测一个因变量(即目标)。那当然就是所谓的监督学习。label_by_func
方法根据给定的函数注释数据集。在这个调用之后,数据集通常被转换成(features, target)
元组。
我们看到label_by_func
方法接受一个函数作为参数,并将其应用于训练集和有效集。我们的工作是设计一个函数,在我们想要标记某种形式的数据集的任何时候为我们的目的服务。在后面的“把它们放在一起”例子中,我们展示了创建这样一个函数是多么简单。
创建批次
最后只剩下一步;将数据集分成几批。为此,我们可以利用 PyTorch 的TensorDataset
和DataLoader
类。
这是这个链中的最后一个方法,因此,我们将其命名为“build”。它创建训练和有效数据集,有了它们,就很容易用已知的批量实例化相应的 Pytorch **DataLoader**
。请记住,我们现在已经标记了数据,因此,self.train_data
是一个由features
和target
变量组成的元组。因此,self.train_data[0]
保持features
并且self.train_data[1]
保持目标。
了解了这一点之后,让我们用一个简单的例子来说明这一点。
在本例中,我们创建了一个包含三列的虚拟数据集,其中最后一列存储了目标或依赖变量。然后我们定义了一个get_label
函数,它提取最后一列并创建一个 features-target 元组。最后,使用方法链接,我们可以很容易地从给定的 pandas DataFrame
中创建我们需要的数据加载器。
结论
在这个故事中,我们看到了在拟合任何模型之前,数据处理的四个必要步骤是什么,假设数据集是干净的。虽然这是一个玩具示例,但它可以用于并扩展到各种各样的机器学习问题。
此外,还有一些步骤没有在本文中介绍(例如,计算机视觉的数据规范化或扩充),但本文的目的是提供一个关于如何构建代码来解决相关问题的总体思路。
我叫 Dimitris Poulopoulos,是希腊比雷埃夫斯大学BigDataStack的机器学习研究员和博士(c)。我曾为欧洲委员会、欧盟统计局、国际货币基金组织、欧洲中央银行、经合组织和宜家等主要客户设计和实施人工智能和软件解决方案。如果你有兴趣阅读更多关于机器学习、深度学习和数据科学的帖子,请在 twitter 上关注我的* 中 、LinkedIn或@ james2pl**。***
数据科学家应该知道的 Git 分支的 4 个步骤
机器学习项目的分支、合并和标记介绍
我相信使用版本控制,不管项目有多小。当你在进行任何类型的快速、敏捷或迭代开发时,这一点尤其重要。训练模型和超参数调整是您想要多个实验和里程碑快照的主要例子。
我最近写了关于数据科学家应该知道的 6 个 Git 命令的文章,作为版本控制新手的入门读物。在本文中,我们将探索一些新的命令来帮助您管理项目的多个独立版本,并将它们合并在一起。
如果您是 Git 新手,您可能想知道什么是分支?可视化分支如何工作的最简单的方法是时间旅行电影的情节。有主时间轴,在 Git 中称为主。当你想做一些新的事情时,你可以从主时间线分支到另一个时间线。主时间线和备用时间线仍然平行前进,但是在任何时候,你都可以跳回到主时间线。当你在 Git 中这样做时,你可以选择是否合并你的修改。
它看起来像这样:
布朗博士解释了错误合并马蒂·小飞侠 1985 年分行的后果。
玩笑归玩笑,理解如何以及何时分支对于使用 Git 至关重要。我将带你经历我在项目中使用的四个步骤。
1.树枝
正如我前面提到的,当您创建一个新的 repo 时,您会自动在主分支中工作。通过在命令行中键入以下内容,您可以看到这一点:
git branch
这应该会返回项目中的所有分支。你现在工作的那个是绿色的。
在这一点上,我们应该花一点时间来讨论什么时候应该创建一个新的分支。比方说,你正准备开发一个新的特性或者重构一些东西。当你解决问题的时候,你不想担心签入错误的代码,所以做一个沙箱来试验是个好主意。把一个分支想象成你的主项目的一个拷贝,在那里进行修改是安全的,不会影响主代码库。
我对我的分支使用特定的命名约定。要创建一个新的分支,您将使用前面的命令,但是将分支的名称添加到其中,如下所示:
git branch v0.1.0-feature
如您所见,我使用软件版本约定来命名我的分支。我从v0.1.0
开始,一路向上。因此,下一个分支将是v0.2.0
,直到我达到一个新的完整版本,如v1.0.0
。有时,您需要对现有版本进行增量更改。在这种情况下,我将创建一个名为v0.2.1
的分支。
如果您在一个组中工作,您可能希望指定一个人作为回购所有者,并将他们指定为可以创建新功能分支的人。团队中的每个人都可以直接在那个分支中工作,或者创建他们自己的子分支。在这个场景中,我会创建一个名为v0.1.0-features-jf
的个人分支,或者像v0.1.0-features-create-new-model
一样添加一个特定的特性名称。
随着时间的推移,您将拥有项目所有主要特性版本的完整历史。它可能看起来像这样:
2.检验
创建分支只是等式的一部分。如果你想在新的分公司工作,你需要先做一个检查。签出分支是通过以下命令完成的:
git checkout v0.1.0-features
结帐完成后,您将在控制台中看到一条确认消息:
现在,如果您在控制台中键入git branch
,它将突出显示您正在工作的新分支:
在签出分支时,有几件事你应该记住。
- 您应该只在提交任何阶段性更改后签出新的分支。Git 有时会将这些带到您要切换到的分支。如果未选中此选项,它会破坏您正在工作的分支。
- 如果您有新的未跟踪的文件,它们将不会被触及。即使切换分支,这些新文件仍将存在。因此,还必须注意 stay 文件,这些文件也可能破坏要切换到的分支。
在切换分支之前,最好使用git status
,并确保在使用checkout
之前一切正常。
最后,如果您正在处理一个从在线回购中下载的项目,您可能没有对所有分支的本地访问权。您可以使用git fetch --all
下拉引用到远程源的分支,然后检查您想要的分支。
3.合并
所以当你在一个新的部门工作了足够长的时间后,是时候保存你所做的一切了。虽然提交变更已经为您逐步完成了这一点,但是在某些时候,您会想要将变更合并回主分支。当您在工作分支中完成更改,并且代码稳定后,就可以用下面的命令检查主分支了:
git checkout master
进入主分支后,可以使用以下命令合并特征分支更改:
git merge v0.1.0-features
在使用命令之前,您总是希望位于将接收合并的分支中。假设所有的东西都被正确地合并了,那么您现在应该在主分支中拥有来自您的特性分支的所有代码。
如果您遇到冲突,控制台会告诉您问题出在哪里。你可以使用各种策略来解决这些问题,这些策略现在有点超出范围。
4.标签
这个过程的最后一部分是标记您所做的更改。标记类似于分支。标签代表项目在给定时间的快照。既然您已经将一个特性分支合并到了主分支中,那么现在是创建该版本标签的好时机。因此,当您仍然在主分支中时,在命令行中键入以下内容:
git tag v0.1.0
注意,我没有在标签名中添加feature
。这有助于表示项目中已完成且稳定的点。因此,当有人签出项目时,他们可以立即从主分支以最新的稳定状态构建项目。同样,如果您希望他们只使用代码库的特定状态,比如在特定日期发布的内容,他们可以切换到相应的标签。
你可以在任何时候看到一个项目中的所有标签,只需在控制台中输入git tag
。
就像git branch
一样,您将看到当前 repo 中本地所有标签的列表。
冲洗并重复
总而言之,在 Git 中进行分支时,有四个基本命令:
branch
创建新的工作分支- 切换到一个新的分支
merge
将代码变更从一个分支复制到另一个分支tag
保存代码的快照
现在,您可以执行步骤 1–4来继续创建新的工作分支。请记住,在分行工作时,您可以像平时一样提交。使用这些提交和它们的注释来跟踪您在稳定发布中所取得的进展。
最后要指出的是,永远不要将代码提交给标签。修补一个标签的正确方法是检查它所来自的分支,并从那里创建一个新的分支。例如,标签v0.1.0
对应于分支v0.1.0-features
。这意味着您将把补丁代码添加到一个新的v0.1.1-features
分支,然后将这些更改合并回master
,并将其标记为v0.1.1
。
如果master
分支在你正在修补的特征分支之前;您需要决定是从当前分支标记还是合并到主分支,并在下一个版本的标记中包含这些更改。
做这件事没有一成不变的方法,这完全取决于规模有多大,有多少人在回购中工作,以及谁负责管理回购的合并。
虽然您可能想要merge
对master
分支的更改,但是它也可能需要合并到额外的特征分支中。所以为了简单起见,我只在迫切需要更新产品而不能等到下一个版本时才修补标签。
我唯一的硬性规定是没有标签项目的任何代码或输出都不能投入生产。重要的是要知道什么版本是活动的,并有一个快照以防出错。这开始进入事情的 DevOps 方面。如果你想了解 DevOps 在过去几十年中是如何发展的,一定要看看我的文章“devo PS 文化的演变”
如果您是 Git 新手,或者在使用分支时需要一些帮助,我希望这有所帮助?如果你在下面留下评论,我很乐意回答你提出的任何问题。
4 种类型的附加特征重要性
内容:
- 哪个目的对于可变的重要性? 1.1 设定你的目标…
1.2…通过选择象限 - 4 个面向目标的象限 2.1 沙普利效应区
2.2 SHAP 区
2.3 沙普洛斯区
2.4 塞奇区 - 每个象限的 Shapley 解 3.1 Shapley 值
3.2 应用于特征属性 - 未来展望和要点信息
- 参考文献
由作者提供的来自datajms.com的附加特性重要性的 4 个目标导向象限的概述。
你可能听说过特征重要性方法:有很多这样的方法,它们对于变量选择和模型解释非常有用。但是还有更多:增加可变重要性的景观最近已经变得结构化并系统化。
这篇文章不仅仅是 SHAP 的另一篇文章,它提出了相似的观点,分享了一个重要的组成部分: Shapley 值。提出了一个结构化的 2 乘 2 矩阵,以便根据目标和范围更好地考虑可变的重要性。侧重于附加特征归因方法,4 个确定的象限与他们的“最佳”方法一起提出:SHAP,夏普利效应,夏普勒斯和最近的 SAGE。然后,我们将研究 Shapley 值及其性质,这使得 4 种方法在理论上是最优的。最后,我将分享我对可变重要性方法的看法。
1。哪个目的的重要性可变?
那么,什么是可变重要性,它们应该具有哪些性质?我们将关注具有以下两个要求的可变重要性:
- 特征属性:表示我们的模型 f 的兴趣数量对每个特征的依赖程度。
- 相加重要性:将重要性相加应该产生一个有意义的量(通常是模型 f 的感兴趣的量)。
虽然特征归属属性是可变重要性的本质,但是附加重要性要求更具挑战性。更著名的可变重要性方法打破了它:Breiman 随机森林变量重要性、特征切除、排列重要性等。让我们关注这两个属性的可变重要性。
1.1.设定你的目标…
让我们来关注一个重要的概念:兴趣量。感兴趣的量是您想要“分割”为变量总和的度量。如果你觉得这个定义太模糊,你会喜欢下面的 Shapley 值部分。
选择感兴趣的数量是下一步,应该符合你的目标。不同视角对应多种选择:
- 局部与全局范围:变量重要性应该对数据集的每一行求和,还是在总体范围内求和?局部范围适用于关注一个数据点的相关情况,或应沿着一维分析重要性的情况。而全局范围与用于高层决策的汇总指标相关:变量选择、因素优先级等等。
- 灵敏度与预测能力度量:变量重要性应该是模型 f 如何变化的度量,还是预测性能如何随之提高?从灵敏度的角度来看,重要性应该集中在用 f 计算如何依赖一个变量。而预测能力方法设置重要性,以说明一个变量对提高预测性能(减少损失函数)有多大贡献。
1.2.…通过选择象限
这些局部与全局范围和灵敏度与预测能力指标定义了一个 2×2 目标导向矩阵。每个象限都以重要性度量命名,从理论上讲,重要性度量对于其感兴趣的数量是“最优的”。这些方程是每个感兴趣的量的加法分解的简化版本。
更具体地说,让我们介绍一些符号。假设从你的变量 X=(X₁,X₂,…,Xᵣ) ,你尝试用你的模型 f(X)∈ ℝ最小化损失函数 l(y,f(x)) 预测 Y 。 x 和 y 是指一个数据点,而 X 和 Y 是总体水平(随机变量)。𝔼和𝕍分别表示一个变量的期望值(“平均值”)和方差。
附加变量重要性的 4 个面向目标的象限,带有感兴趣的数量和附加细分。每个象限都用象限的最优方法命名。图来自datajms.com,由作者提供。
2.4 个以目标为导向的象限
让我们来看看 4 个象限和它们解决的不同问题。我们将按时间顺序进行这次旅行,因为它讲述了两个不同的研究团体如何最终相遇的好故事!
2.1.沙普利效应区
改进 Sobol 指数(1993) [1],Owen 在 2014 年引入了一个重要性度量[2],该度量已由 Song 等人在 2016 年开发并命名为“Shapley 效应[3]”(另见 Iooss 等人在 2017 年的进一步工作和数值实验[4])。它来自于敏感性分析和不确定性量化领域,旨在量化模型 f (例如一组复杂方程的计算机模拟)的输出在多大程度上取决于 X 输入参数。Shapley 效应也完全与机器学习相关,它关注于学习模型 f 的变化在多大程度上依赖于变量 X 。
感兴趣的量是𝕍( f(X) 。方差是量化变异的自然选择。请注意,在敏感性分析社区中,指数通常由总方差归一化,因此所有变量的重要性总和为 1(或 Sobol 指数“接近”1)。
通过查看 4 个象限,一个问题出现了:为什么不选择𝔼( f(X) 作为感兴趣的量?这绝对是全球性的。然而,它与解释变化无关:正的和负的变化将消失在 0 全局贡献中。
让我们继续到 2017 年,开始机器学习社区的 Lundberg 传奇。
2.2.SHAP 区
由 Lundberg 于 2017 年设计并实现[5],shap a 具有局部敏感性焦点。请注意,尽管 shap 是在机器学习会议上发表的,但它并不涉及 Y 目标或模型 f 的任何学习。这就是为什么我能够把它应用到一个非学习的、基于专家的算法中,用于新冠肺炎患者定位。然而,它非常适合机器学习社区,因为它的快速模型特定的实现。
兴趣量坚持最自然的选择: f(x) for x∈ X 。与全局范围不同,在这里同时有正面和负面的贡献是有意义的。了解变化的方向是完全相关的,并允许对 shap 值进行良好的可视化探索(在 shap 包中实现)。
2.3.沙普罗斯区
2020 年发表在 Nature[6](但预印在 2019 年),Lundberg 等人提出了一项创新!虽然本文主要关注基于树的模型,但提出了一个新的想法:使用 shap 将模型误差分解为特征贡献(参见本文的 2.7.4 和图 5),这对于生产中模型的监督性能监控非常有用。我虚构了 SHAPloss 这个名字,以强调所实现的不同目标,尽管实现是在 shap 包内完成的,只需改变 TreeExplainer 中的 model_output 参数。
感兴趣的量是对于 (x,y)∈ (X,Y)的局部损失-l(Y,f(x))* 。*注意 l 自然可以是分类问题的对数损失,同时是回归的 MSE。添加了负号,因此较大的正贡献ϕᵢ意味着可以大大提高性能的特性。
2.4.圣贤区
在 2020 年 4 月提交的预印本[7]中,Covert 和 Lundberg 等人介绍了 SAGE(Shapley Additive Global importancE),一种 SHAPloss 全局公式的解决方案以及计算它的有效方法。请注意,本文远远超出了 SHAPloss 简单的局部到全局的概括,但它也包括对现有重要性方法的回顾,并介绍了理论上的通用预测能力。此外,SAGE 论文明确提到了我们所谓的 Shapley 效应区,解释了 SAGE 在目标上的不同之处。在某些方面,它关闭了我们已经探索过的四象限回路。
感兴趣的量是𝔼[-l(y,f(X)) ],局部 SHAPloss 公式的自然集合。与 SHAP 到沙普利效应的转换不同,这里采用原始预期。这是因为几乎没有正负湮灭,因为增加一个变量通常不会增加损失。
3.每个象限的 Shapley 解
既然已经设定了目的及其感兴趣的数量,Shapley 值[8]理论为每个象限提供了给定期望属性的最佳解决方案。让我们先介绍一下 Shapley 值,看看它如何应用于各种感兴趣的量。
3.1.沙普利值
沙普利值 ϕᵢᵐ 是一种“公平”分享联盟pᵣ={1m(pᵣ获得的利益数量的归属方法,2,..,r} 每个实体之间 i∈ Pᵣ 。 m(u) 是返回联盟的利息数量 u 的函数。联盟是一组实体:有 2ʳ可能的联盟,包括∅和 Pᵣ。最后,让我们用 Sᵢʳ 来表示不包含实体 i 的所有可能联盟的集合。
沙普利值 ϕᵢᵐ 是唯一满足 5 个期望属性(查看 SAGE 论文[7]的 3.1 了解其含义)的数量权重,这 5 个属性分别是对称性、线性度、单调性、虚拟性以及效率,我们在这里写道: m(Pᵣ)=m(∅)+ ∑ ϕᵢᵐ
关于这个 Shapley 值的公式,有很多要讲的。但这有点跑题了,我更愿意把重点放在如何将 Shapley 的想法应用到 4 个象限上。
3.2.应用于特征属性
在我们的上下文中使用 Shapley 值意味着 i 实体就是 Xᵢ 变量。剩下的两个任务是选择感兴趣的量,并为变量 u 的每个联合定义 f 。为我们的 4 象限选择的解决方案是沿着缺失变量取期望值:fᵤ(x)=𝔼(f(x|xᵤ=xᵤ)。有关更多信息,请参见 SAGE 文件中的详细信息。
4 个感兴趣的量转化为 4 个 m(u) 函数,这导致了象限的 4 个名称:具有期望属性的可变重要性方法!
让我们用更精确的感兴趣的量 m(u) 重写 2 乘 2 矩阵,这些量是 f 和所有特征联盟 u ( u∈ { ∅、 { X ₁}、{ X ₂}、..、{ X ₁ ,X ₂}、..}).
可加变量重要性的 4 个面向目标的象限,详细的公式。图来自datajms.com,作者提供。
四个沙普利值ϕᵢshap(x)、 ϕ ᵢ 损失( x ) 、 ϕ ᵢ EFF 和 ϕ ᵢ 圣人是每个象限的“最优”解。请注意,这些值之间有两个链接:
- ϕ ᵢ 圣人= 𝔼[ϕ ᵢ 损失( x )]
- 潜在地,如果损失函数 l 是 MSE,我们有ϕᵢeff=ϕᵢsage 与y=f(x)。
4.未来展望
我们刚刚看到,对于每个象限,“最佳”解决方案已经被定义,并且实现是可用的。那么,完整的故事被讲述了吗?
一方面,我认为附加重要性测量领域已经达到了一个成熟的里程碑,最佳地填充了 4 个象限,从而结束了循环。在 SAGE 的文章之前,我不知道敏感性分析和预测能力重要性之间有任何明确的联系。
另一方面,在可变重要性和特征属性方面仍有改进的空间,包括更好地利用这些技术和探索此范围之外的价值:
更好地使用象限中的方法:
- SHAPloss 在数据科学社区中的传播:尽管 SHAP 在数据科学社区中的采用率非常快,但 shap loss 目前仍不为人所知(除了 Hall 的一本励志笔记本)。我看到了对生产中的模型进行受监督的(当基本事实标签已知时)性能监控的价值。
- 计算效率的提高:除了基于树的模型之外,这些方法都是计算密集型的,并且会很快变得难以处理。实现和统计估计的改进可以提高可用性(参见最近的工作[9])。
除了 2 乘 2 矩阵:
- 基于公平的利益数量:为什么不设想其他列?理解模型行为和模型性能是首要的重要步骤。但是,负责任的数据科学也包括相关的偏见和公平性监测。理论上,似乎可以选择一个基于公平的相关兴趣量 m(u) 并构建其 Shapley 值,以查看“不公平”将如何在特征之间划分。伦德伯格用人口均等指标开辟了道路,巧妙地保持在 SHAP 区域内。
- 探索非附加的特征归属方法。如果使用乘法分解或完全不同的重新加权方法[10]来完成,量化感兴趣的量在多大程度上依赖于输入特征仍然是广泛的研究和实践领域。
一些外卖信息。我希望这篇文章:
- 启发你理解变量重要性的不同目标和范围。
- 让你相信,自从 Sobol 在 20 世纪 90 年代开创了这个领域以来,相加重要性度量领域无疑比以往任何时候都更加成熟。
- 根据你的目的,让你思考选择最有效的象限。
对 SHAP 和沙普利效应区的结果和代码感兴趣吗?你可以查看我的文章新冠肺炎患者定位算法的可变重要性。此外,您可以查看 SAGE 论文[7]以获得更多非最优但计算量更小的方法的示例,以及它们如何适合 2×2 矩阵。
5.参考
[1]: 索博尔,I. M. (1993)。非线性数学模型的灵敏度估计。数学建模和计算实验,1(4),407–414。顺便说一句,我找到的唯一在线版本是一份由 I. M. Sobol 本人手写注释的复印件,寄给了著名的敏感性分析研究员 Andrea Saltelli。
[2]: 欧文,A. B. (2014)。索博指数和沙普利值。SIAM/ASA 不确定性量化期刊,2(1),245–251。
[3]: Song,e .,Nelson,B. L .,& Staum,J. (2016)。全局灵敏度分析的 Shapley 效应:理论和计算。SIAM/ASA 不确定性量化期刊,4(1),1060–1083。
[4]:伊奥斯,b .,T15 普里尔,C. (2019)。相关输入敏感性分析的 Shapley 效应:与 Sobol 指数的比较,数值估计和应用。国际不确定性量化杂志,9(5)。
[5]:伦德伯格,S. M .,T16 李,S. I. (2017)。解释模型预测的统一方法。神经信息处理系统进展(第 4765-4774 页)。
[6]: Lundberg,S. M .,Erion,g .,Chen,h .,DeGrave,a .,Prutkin,J. M .,Nair,b .,Katz,r .,Himmelfarb,j .,Bansal,n .,& Lee,s . I .(2020)。用可解释的人工智能对树木从局部解释到全局理解。自然机器智能,2(1),2522–5839。
[7]:隐蔽,我,伦德伯格,s .,T17 李,S. I. (2020)。通过附加重要性测量了解全局特征贡献。arXiv 预印本:2004.00668。
沙普利,L. S. (1953)。n 人游戏的一个值。对博弈论的贡献,2(28),307–317。
[9]:普利斯克等人最近的预印本将沙普利效应的计算提高了几个数量级:普利斯克,e .拉比蒂,g .&博格诺沃,E. (2020)。灵敏度分析中沙普利效应的计算。arXiv 预印本:2002.12024。
[10]: 巴乔克,f .,甘博亚,f .,卢贝斯,J. M .,&里塞尔,L. (2018)。熵变量促进机器学习中的可解释性&。
进行机器学习的 4 种方式
你的团队处理机器学习的正确方式是什么:研究、开源、托管平台还是 API?
来源:作者
机器学习可能会令人困惑。每个人都用这个词来表示稍微不同的东西,有太多的东西需要跟上。
在你选择供应商或平台来满足你的人工智能需求之前,了解机器学习的哪个“层”最适合你很重要。你可以从头开始构建自己的人工智能,使用现成的产品,或者介于两者之间的东西。
在本帖中,我们将带你浏览四个选项,并通过将它们与交通工具进行比较,帮助你确定哪一个最适合你。
机器学习的不同“层次”
就像不同的交通工具一样,你可以用不同的方式使用机器学习。来源:作者
- 开发新的语音转文本算法等新颖的研究就像制造汽车一样。
- tensor flow 等开源框架类似于拥有一辆汽车。
- AWS SageMaker 等托管平台就像打车。
- Google Translate 等 API类似于乘坐航班。
你可以在不同的层面建立一个机器学习解决方案。当你进入更专业的层时,更多的特性可以直接工作,但是你得到的灵活性更少。
每一层都建立在前一层的基础上:API 通常建立在托管平台上,托管平台建立在开源框架上,开源框架建立在研究的基础上。
不同的问题最好由不同的层来解决
哪一层最适合你,主要取决于你要解决的问题。
然而,并非所有的层都是相同的大小:例如,一个问题被一个托管平台干净地解决,这实际上是非常罕见的。
不同的问题更适合不同的层次。“托管平台”层比许多人想象的要小。来源:作者
因为托管机器学习平台背后的公司使用激进的营销策略,所以人们经常高估这些平台的价值。事实上,托管平台通常会让您面临使用现成 API 的最坏情况,同时无法完全解决您在使用开源框架构建定制解决方案时必须接受的棘手问题。
让我们深入了解一下每一层。
做人工智能研究就像制造你自己的汽车
如果你想使用人工智能,你可能会想雇佣一个博士团队。但这就像雇佣一个机械工程师团队,让他们为你制造一辆汽车,然后 24/7 全天候为你服务。
如果你是一名参加国际比赛的赛车手,这可能是合理的——但在其他方面可能就不合理了。类似地,像谷歌这样的公司有研究团队,他们通过开发新的算法和架构来推动人工智能的基础。
这项工作成本很高,往往需要几年甚至几十年才能创造出真正的价值。如今,大多数公司甚至不需要考虑采用这种方法。除非你在你的领域取得领先的 1%的成绩,否则拥有一个专门的研究团队是不太可能有意义的。
你可能想做你自己的人工智能研究,如果:
- 你正在创造一个 Siri 的竞争对手,你希望它在语音到文本的翻译方面比 Siri 更好。
- 你正与其他投资银行展开正面竞争,并希望让你的交易算法获得优势。
样本团队结构:
- 一个研究工程师团队:博士、博士后和教授。
- 能够将研究成果转化为机器学习解决方案的数据、机器学习和 devops 工程师团队。
使用开源框架就像拥有自己的汽车一样
随着学术研究的成熟,它演变成像 Tensorflow、PyTorch 和 Keras 这样的开源框架。世界各地成千上万的开发者免费为改进这些框架做出贡献。
任何人都可以免费下载并使用这些开源框架。这些与亚马逊、谷歌和微软等昂贵的托管平台使用的强大“引擎”完全相同。但是您可以灵活地按照自己的方式构建系统。
但就像拥有自己的汽车一样,这也有一些不利之处。如果你有一辆车,你需要学习如何驾驶或者雇一个司机。要使用 Tensorflow 这样的工具,你需要学习编码或者雇佣一个开发者。
如果你有一辆车,你还需要担心加油、更换轮胎和找到一个安全的车库等事情。如果你建立自己的定制机器学习解决方案,你还需要建立一个合适的基础设施并维护它。
你可能想用开源框架开发你的机器学习解决方案,如果:
- 您希望改进一个非常适合您的业务的工作流程。
- 您拥有关于内部问题的数据和领域知识。
- 你不怕承担复杂的软件项目。
样本团队结构:
- 内部:devo PS 工程师、机器学习工程师、数据科学家、项目经理和主题专家的团队。
或
- 外部:像美国这样的机构,他们使用开源框架构建你的定制 ML 解决方案。
使用托管平台就像打车一样
有时候你很快就需要某样东西,你愿意为此付出更多。如果你需要做一次快速的一次性旅行,你可以乘出租车。但是如果你每天都坐长途出租车,那么买辆车可能更划算。
同样,像 AWS Sagemaker、Dataiku、Knime、Alteryx 这样的托管平台,可以让你更快上手,并提供一些预建的组件帮助你更快上手。
但是,尽管这些平台可以节省你的时间,但它们往往比他们的营销团队愿意承认的要有限得多。此外,如果你被“锁定”在其中一个上,费用可能会很高。就像出租车一样,如果你一直需要它,那么它可能是错误的解决方案,你可能会比你预期的更快达到它的极限。
您可能希望使用托管平台,如果:
- 机器学习不会成为你业务的重要组成部分,也不会成为竞争优势的来源。
- 您希望将机器学习应用于已经很好理解并且已经在其他地方解决的标准问题(例如,推荐系统、流失分析、需求预测)。
样本团队结构:
- 至少一个机器学习工程师,一个开发人员,一个 DevOps 专家。
使用 API 就像坐飞机一样
如果你只是需要定期从伦敦到纽约,那么公共交通可能会很好地满足你的需求。这是最不灵活的运输方式,但它做一件事,而且做得很好。
类似地,像 Google Translate 这样的 API 和 web 应用程序最容易插入到您现有的基础架构中。它们还可以根据您的需要进行扩展,并提供无可匹敌的质量水平。
但它们也是最不灵活的。如果您有任何定制需求,那么 API 可能对您来说不够灵活。此外,您还需要支付额外费用:例如,使用翻译 API,您可能需要为翻译的每个字符付费。
您可能想要使用 API,如果:
- 你有一个电子商务平台,你想让用户看到其他语言评论的自动翻译。
- 你经营着一家物流公司,你想要自动优化送货路线。
样本团队结构:
- 一个开发人员将 API 集成到您的系统中。
后续步骤
一旦你选择了对你来说最有意义的“层”,那么你就可以开始进入使用什么样的编排平台、工具或管理平台的细节。
如果您仍然不确定,请与我们预约免费电话来讨论什么可能适合您!
本文原载于【datarevenue.com。
我在学习 Tableau 时经常犯的 5 个错误
入门
这些错误是可以避免的
今年早些时候,我醒来说今天将是我学习 Tableau 的一天。我卷起袖子,直接看了我在 Youtube 上找到的第一个初学者教程,标题很吸引人,比如“如何在一天内学会 Tableau”大约 30 分钟后,我发现自己闲下来了(不久后我关上了笔记本电脑),开始了构建我的第一个 Tableau 仪表板的缓慢而痛苦的跋涉。事实证明,学习 Tableau 并不是花了我一天的时间,而是花了我两个月的时间,因为我在学习过程中不断地犯技术性和非技术性的错误。
这是我在学习 Tableau 时犯的五个最大的错误,以及如何避免它们以成功和有效地掌握这个流行的 BI 工具。
错误 1 |学习我不需要的画面特征
当我排队等候我的第一个 Tableau YouTube 教程时,我很难认识到我应该首先学习 Tableau 的哪些功能。问题还不止于此——我真的不知道我的数据故事中的问题。所以当我坐着看教程的时候,一切看起来都是相关的。我会失去兴趣或被拉进新的教程,这是有道理的——我缺乏让学习过程感觉个性化所需的洞察力和专注度。
虽然作为初学者,这个错误可能很难完全避免,但我建议花点时间思考一下你认为 Tableau 可以帮助解决的数据故事中的问题。这些可能看起来像:
- 使用仪表板使您的数据演示更具互动性和吸引力。
- 快速创建漂亮的分层地图。
- 利用 TabPy 加强你的预测可视化。
您将希望从学习直接涉及您的分析过程中的问题的特征开始。Tableau 的功能非常丰富,在学习如何在 Tableau 中使用自定义地图框样式之前,理解细节层次(LOD)表达式可能会对您有所帮助。
错误 2 |用不相关的数据集练习
要学习 Tableau,您需要一个可靠的数据集,最好是一个“真实的”数据集,以便在工作中加以利用。我最初的实践数据集大部分都非常干净,从来不需要连接。
如果你没有现成的数据集,你可以在这里、这里或者这里找到很棒的公共数据集。我建议使用与您办公桌上最常出现的数据类型相似的数据集。对我来说,这是调查数据,所以我开始确保我的实践数据集都有李克特量表和人口统计数据。对你来说,这可能意味着寻找包含大量地理、数字或布尔数据的数据集。
错误 3 |选择了错误的教程
这个错误我犯了好几次了。在这个过程中,我了解到一些教程涵盖了你所知道的关于 Tableau 的一切,而其他教程只涵盖了最基本的东西。在一个快速的“如何学习 Tableau”在线搜索中,你会看到这两个极端的广泛代表。
这两个教程
选择合适的教程是一个寻找两种类型视频的练习。第一个是介绍性教程,教你 Tableau 的基本功能。这个视频应该教你如何下载 Tableau,连接你的数据源,建立你的第一个工作表,使用流行的功能,如可视化“演示”,并了解 Tableau 的尺寸和措施。对于这种类型的视频,我建议在这里观看 Edureka 的免费 YouTube 课程的前 1.5 小时。
第二种类型的视频专用于您的数据分析。从错误#1 和#2 中吸取教训后,我开始关注如何利用 Tableau 来可视化我的调查数据。我在史蒂夫·韦克斯勒在 2014 年 Tableau 会议上的演示中的上传中找到了完美的内容。这个视频是非常具体的调查,并成功地帮助我清理和重塑我的数据,然后上传到 Tableau。对于第二种类型的视频,我建议在 YouTube 上寻找小众教程,这些教程使用与你相似的数据集,并针对你的特定行业。
提示:参与使用你将使用的 Tableau 平台的教程。例如,您可能会发现您在 Tableau 桌面教程中学习的功能在 Tableau Public 上不可用。这肯定会令人失望!
以下是您在学习过程中可以利用的一些其他资源:
- 现场网络研讨会您可以提出您的问题,并获得现场解答!对于一些网络研讨会,您甚至可以下载一个演示数据集,与演讲者一起工作。
- 免费培训视频如果你正在使用 Tableau Desktop,这些视频会特别有帮助。
一旦你完成了你的第一个项目,你可能会发现 Tableau 是多么多才多艺,然后更有意识地指导你的学习。您可能希望搜索教程来帮助您成为计算奇才或在 Tableau 中编写令人印象深刻的 Python 代码行的专家。
错误 4 |没有花足够的时间学习加入
由于 SQL 培训,我现在对内部、左侧、右侧和完全外部连接有了更多的了解,但在我刚开始学习 Tableau 时,我对连接一无所知。我在几个教程中看到了这个特性,但是错误地把它归类为另一个我不需要马上知道的特性。伙计,我错了。我很难合并数据集或完全避免连接数据。回顾过去,最令人沮丧的是,连接是 Tableau 中最容易实现的部分,也是数据分析不可或缺的部分。花时间理解连接以及如何应用它们是至关重要的。
错误 5 |建立一个我无法分享的可视化
我花了几个小时制作的第一个 Tableau 仪表盘最终成了一个局部半身像,因为我很快发现我的客户没有简单的方法来查看交互式仪表盘。因为数据是保密的,我不能把我的作品上传到 Tableau Public。我必须考虑的唯一选择是指导我的客户将 Tableau Reader 安装到他们的桌面上。我最终决定下载并以 PDF 格式分享我的作品(这是一个悲伤的时刻)。
一个悲伤但坚定的本文作者。我是说,帕格。照片由马太·亨利在 Unsplash 上拍摄
为了避免这个错误,花点时间设想你计划如何交付你的工作。如果你的数据是非机密的,我建议你熟悉 Tableau Public,即使你是在 Tableau Desktop 上工作。
提示:Tableau Desktop 和 Tableau Public 是这家公司最受欢迎的产品。后者是免费的,但对您可以连接的数据源类型有一定的限制,如果您正在处理机密数据,则不适合使用它。根据 Tableau ,“你不应该公布你想保密的机密数据,比如你公司的销售计划或者你的个人财务信息。一旦发布,你就应该知道这些数据不再是隐私了。”
Tableau Desktop 不是免费的,但是如果你是一个注册学校的学生,你很幸运。学员可以下载访问 Tableau 桌面一年,免费 。如果你不是学生,Tableau 确实提供了 14 天的免费试用,你可以在这里下载并用来练习。
我建议访问您将在几个月后使用的平台。
最后…
我在学习 Tableau 时犯的这五个错误成为了加强我自学过程的教训。在我学习新技术的时候,对它们的反思帮助我成为了一名更好的学生。现在,我很想听听你的意见—
你是否曾经从事一项新技术,但在学习过程中感到停滞不前?这是一项其他人似乎马上就能掌握的技术吗?
采用数据科学的 5 个阶段
你的公司数据流畅吗?
有些公司是为数据科学而建立的。其他人需要做出一些重大的基础设施改变,以便数据科学为他们有效地工作。在本文中,我概述了公司在利用数据科学的过程中经历的数据流畅性的 5 个阶段。
如果你是一名数据科学家,你的公司所处的数据流畅性阶段将极大地影响你所做的工作类型。如果您是数据科学经理或负责公司数据战略的人,这篇文章可以作为将您的组织带到下一个级别的路线图。
第一阶段
第一阶段:数据收集
这是数据科学采用生命周期的基础。如果您的公司没有高质量的数据,您就无法执行高质量的数据科学。处于这一阶段的组织认识到数据在未来可能很重要,因此他们开始收集数据。
大多数公司已经过了这个阶段,但是重新评估你的数据收集协议和最佳实践并没有坏处。
第二阶段
第 2 阶段:数据汇总
收集数据是不够的。组织意识到,除非对收集的数据进行清理和组织,否则这些数据毫无用处。数据科学家和分析师可以处理杂乱的数据,但通常情况下,至少需要做一点工作。
处于这个阶段的公司通常会雇佣一名数据架构师,并开始将他们的数据转移到一些数据库基础设施中。
这个阶段的开销可能比人们预期的要多。许多不同的系统可以通过多种方式收集数据。数据工程师需要获取所有这些数据,并对其进行格式化,使其具有凝聚力。
第 3 阶段
阶段 3:分析洞察力
公司认识到数据可以帮助他们改进决策。在这个阶段,洞察通常是描述性的,并且基于在数据透视表中发现的趋势。人类的决策正在被分析洞察力所补充。这项工作通常可以由一组分析师来完成。
这种洞察力的例子通常出现在金融或营销领域。公司基于这种类型的分析对客户进行细分,并在不同的市场销售不同的产品。
第四阶段
第 4 阶段:高级分析
你开始使用更先进的机器学习模型来改善决策。这个阶段的特点是模型在人的监督下做出决策。组织需要认同和信任他们的数据科学团队,才能达到流畅的这一阶段。在这个级别,数据科学家是必须的。
高级分析将数据科学视为一种产品。数据科学家制作供自己或他人“使用”的研究和模型。例如,如果一家卡车运输公司建立了一个模型,可以预测车辆何时需要新轮胎。这个模型,结合人类的评估,将决定何时更换轮胎。
第五阶段
阶段 5:分析集成
数据流畅性层级的最后一个阶段是将数据科学完全整合到业务的某些领域中。在这里,机器学习模型被投入生产,并在没有人类直接监督的情况下做出决策。在这个阶段,你的数据科学家(或机器学习工程师)正在与你的工程团队合作,让数据科学成为一种服务。
分析集成将数据科学视为一项服务。这方面的一个例子是一个在线网站的推荐系统,它根据顾客的喜好和过去的购买情况向顾客展示不同的产品。
https://www.youtube.com/watch?v=BXmEmIK5aqI
想想你的公司在数据流畅的阶段。你有合适的资源,知道如何将你的能力提升到一个新的水平吗?
大数据的 5 个 v 隐藏着一个陷阱
商业科学
如何有效利用大数据
被 数据驱动是必须的 。明智的决策制定,尤其是在自动化商业智能的支持下,可以带来更高的投资回报率和更好的业务成果。
从人工智能工程到超自动化,在所有业务领域使用数据的更有效方法是 Gartner 2021 年每一个顶级战略技术趋势的核心。
后 Covid 时代,以数据为中心不再是发展的选择。这是要求。
尽管如此,哈佛商业评论报告称,77%的高管认为大数据和人工智能计划是他们最大的挑战。更糟糕的是,这一比例在过去几年中呈指数级增长。
这种趋势在未来十年有可能继续吗?随着公司试图转换到数据驱动的思维模式,他们很难有效地使用他们的数据。大多数企业不知道如何摆脱的第 22 条军规。
你已经收集了大量的交易数据。现在怎么办?
Arturo Rey 在 Unsplash 上拍摄的照片
收集和访问数据不是挑战 。企业每天都会产生数十亿个数据点。到 2025 年,每天将产生 463 艾字节的数据。这比存储人类历史上所有话语所需的字节数大了 90 倍!这些数据中很大一部分是社交媒体、Tik Toks、电子邮件和自拍,但更多的是交易数据。
尤其是零售企业,它们从交易数据中获得了一些最有价值的商业情报。这曾经是一个简单的过程。我们可以使用 Excel 甚至手工计算需求和理想价格。如今,分析交易数据的传统方法远远不够。数据的规模让我们甚至无法理解这些数字,更不用说从中获得有用的见解了。
大数据改变了问题的性质。
我们不再费力收集数据;我们努力有效地使用它。
一旦你有了这么多的交易数据,感觉就无法理解接下来应该发生什么。
大数据的传统 4v
幸运的是,有一个行业标准可以指导我们。为了有效利用大数据,您需要解决 4 个 Vs。你必须理解、标准化和验证这些元素,否则你将永远无法成功地提取可用的情报。
马库斯·斯皮斯克在 Unsplash 上拍摄的照片
1.卷
你到底有多少数据?当然,大数据意味着大量数据,但是您使用了多少呢?容量决定了您的分析必须容纳多少数据。
2.速度
数据收集、存储和处理的速度有多快?你是接近拥有实时数据,还是有滞后?模型多久需要考虑一次新数据?速度决定了你的分析有多及时和相关。
3.品种
你有什么样的数据?它是如何构成的?你的数据来源有多多样化?数据类型的变化是否足以让 it 部门提供适用的建议?多样性决定了你的分析是孤立的还是整体的。
4.准确性
数据是否正确?有缺失的部分吗?你的数据有多少是噪音?准确性决定了你的分析有多准确。
价值:新的第五代
大数据的这些 V 可能是行业标准,但数据科学家越来越认识到第五个甚至更重要的 V: 值 。
换句话说,在商业环境中,大数据最重要的是你将数据转化为决策的能力,从而提高公司的投资回报率。数据必须是可操作的,并且带来的价值要大于分析数据的成本。在规模上,数据的质量比数量更重要。价值评估可用数据的最终质量。
这第五个 V 对公司来说至关重要。仅仅因为我们根据其他 v 收集了好的数据,并不意味着它实际上是有用的。拿走价值,数据无用。
Daoudi Aissa 在 Unsplash 上拍摄的照片
更好的数据接收+更好的分析=成功
数据的价值因素是为什么如此多的公司在尝试成为有效的数据驱动型企业时失败了。有些人花了太多时间试图管理前四个 v,以至于他们不知所措,无法在价值到来时提取有用的见解。其他人则完全专注于分析,而不担心大数据的基础,因为数据的缺陷会限制其价值。没有完美的平衡,你就丢掉了关键的智慧。
幸运的是,当您自动化基础工作(数据摄取和分析)时,平衡比您想象的要容易。
了解数据接收
数据摄取与您管理和创建数据模型的方式有关。本质上,数据摄取与前 4 个 Vs 有关。这是一个处理数据量、降低速度、根据多样性进行描述并监控准确性的过程。数据被适当地存储起来,任何差异或缺口都被识别和修复。
数据摄取可能非常复杂,但是现在有一些工具可以自动完成这个过程。在 Evo ,我们创建了 EvoFlow ,它编排数据流并运行一系列检查以确保我们使用的数据是有序的。气流和其他工具可以达到类似的目的。这些工具会自动执行并反复检查验证数据是否符合前 4 个 v 的流程,从而让您专注于价值。
自动化分析
然而,关注价值并不意味着你应该牺牲自动化。分析也应该自动化,以避免人为错误。我们发现这是数据科学和商业科学之间的差异:自动化减少了使用和输入错误,这是性能不佳的最重要来源。
平衡这一边的自治系统最大化价值,这是每个公司最应该关心的大数据元素。分析是最终将大量交易数据和其他业务数据归结为洞察力的一步。这就是大数据实际上可以在 KPI 和贵公司的市场成功中发挥作用的地方。如果没有自动化分析,您将永远无法优化值。
避免数据驱动的陷阱:人工智能自动化
照片由 Michael Dziedzic 在 Unsplash
数字化转型仍然是一个难以实现的目标,但通过自动化、人工智能驱动的大数据使用,每个企业都可以成为真正的数据驱动型运营。如果我们试图在没有比人类更快地处理、验证和分析数据的技术的帮助下使用数据,大数据的陷阱只会抓住我们。您不需要淹没在交易数据中,而是挖掘其价值以获得更好的结果。
想更多地了解大规模数据接收和交易数据的技术方面吗?在 Evo 大学参加免费课程!我的同事托比亚·图迪诺是一名数据工程师专家,他深入研究你所知道的一切,开始更有效地使用你的交易数据。我鼓励你报名参加 https://evo.ltd/join 大学。
PS 我定期写商业科学。推荐后续阅读:
数学最优化的悖论
towardsdatascience.com](/supply-chain-optimization-worth-it-or-not-20ae4c6e635) [## 94%完美:2000 亿美元库存问题的惊人解决方案
机器学习和人类经理一起修复供应链
towardsdatascience.com](/94-perfect-the-surprising-solution-to-the-200-billion-inventory-problem-b6ba0bc1417a)
Monthly Business Science in your inbox, new software, and University-level learning:[**Free access**](https://evouser.com/register)Questions? Please reach out on [Linkedin](https://www.linkedin.com/in/fabrizio-fantini/)
SQL Select 语句过程的 6 个步骤
使用简单示例详细解释查询执行顺序
由 Pixabay 授权
介绍
在本文中,我将一步一步地描述对关系数据库执行查询的逻辑过程。为此,我将使用两个简单的非标准化表格:公民和城市。它们描述如下:
表格描述
市民表包含杰出市民的数据和他们居住的城市的识别号,城市是包含城市名称和他们各自的识别号的表。**
数据
假设我们只想知道两个城市的名称,除了圣布鲁诺,那里有两个或更多的居民。我们也希望结果按字母顺序排序。
这是获取所需信息的查询。
SELECT city.city_name AS "City"
FROM citizen
JOIN city
ON citizen.city_id = city.city_id
WHERE city.city_name != 'San Bruno'
GROUP BY city.city_name
HAVING COUNT(*) >= 2
ORDER BY city.city_name ASC
LIMIT 2
查询流程步骤
1.获取数据(从,加入 )
2。行过滤器( Where )
3。分组(分组由 )
4。组过滤器(有 )
5。返回表达式(选择 )
6。指令&分页(指令由&限制/偏移)
第一步:获取数据( From,Join )
FROM citizen
JOIN city
该过程的第一步是执行来自子句的中的语句,随后是 Join 子句。这些操作的结果是得到两个表的笛卡尔乘积。
笛卡尔乘积
一旦来自的和连接被执行,处理器将根据条件得到符合条件的行。**
ON citizen.city_id = city.city_id
合格的行
第二步:行过滤器(Where)
在获得合格的行之后,它被传递给 Where 子句。这使用条件表达式评估每一行。当行的计算结果不为 true 时,它们将从集合中移除。
WHERE city.city_name != 'San Bruno'
第三步:分组(分组依据)
下一步是执行 Group by 子句,它会将具有相同值的行分组到汇总行中。此后,所有的选择表达式将按组计算,而不是按行计算。
GROUP BY city.city_name
第四步:分组过滤(Having)
Having 子句的由一个逻辑谓词组成,它由在组之后处理,并且它不再引用单个行,只能引用行组。
HAVING COUNT(*) >= 2
执行该操作的结果将保持如上图所示的设置。这是因为每个组中都有两个或更多的元素。
第 5 步:返回表达式(选择)
在这个步骤中,处理器评估查询结果将被打印的内容,以及是否有一些函数要在数据上运行,如 Distinct 、 Max 、 Sqrt 、 Date、Lower、等。在这种情况下,select 子句只打印城市名称,并用标识符“city”作为 city_name 列的别名。
SELECT city.city_name AS "City"
步骤 6:排序(Order by)和分页(Limit / Offset)
查询的最后处理步骤处理显示顺序和限制结果集大小的能力。在我们的例子中,要求最多显示两个按字母顺序排序的记录。
ORDER BY city.city_name ASC
LIMIT 2
结果集
结论
更好地理解事物是如何工作的,就能获得更好的结果。对如何执行一个 Select 语句有了基本的理解,就更容易得到预期的结果集。
希望这篇文章对你也有帮助。
数据科学的 7 个习惯
生产力
用这些习惯开始你的每一天
米切尔·霍兰德在 Unsplash 上的照片
每天,我都过着我的日常生活。重复一遍。
在数据科学的世界里,我做的大部分工作是探索未知的领域(读作:数据)。工作流或多或少类似于每个项目迭代。数据收集—探索—流程—建模—测试—部署—监控—展示—重复。我想知道,我怎样才能确保我不是一遍又一遍地重复同样的循环?我怎样才能做得更好?
我找到的答案是:适应我的习惯。您如何:
- 查看和重构数据科学问题,
- 创建相互协作,以及
- 保持有效沟通
是经常被忽视的习惯。请记住,数据科学不是构建最佳模型,而是通过数据产生积极影响。你的习惯会影响你对每个问题的反应。
受斯蒂芬·R·科维的启发, 高效能人士的 7 个习惯 改变了我。对我来说,这仍然是一本很有影响力的书。不管是不是数据科学,在我的工作流程中应用这些习惯可以提高我的工作效率。我想和你分享这些让我保持积极心态的习惯。
以下是我从数据科学的角度对这 7 个习惯的看法。当我们经历它的时候,问问你自己,你需要改变这些习惯吗?
1 —积极主动
嗯,不是那种主动。相反,为了更大的利益采取主动。
数据科学不是做利益相关者想要的事情。它是增长黑客和解决商业问题的工具。没有人比你更了解这些数据。一个高效的数据人员应该就如何利用数据提出建议!人们可能没有意识到数据的力量。
专注于数据能带来什么价值!
“被动型的人受感情、环境、条件和周围环境的驱使。积极主动的人是由价值观驱动的——经过仔细思考、选择和内化的价值观。”—斯蒂芬·柯维
2——以最终目标为出发点
数据科学不是完成你的工作,而是找到实现目标的最佳途径。
回答商业问题时,要想好结果会是什么。没有它,任何项目都不能很好地计划和执行。当我们不知道范围的时候,我们怎么能工作呢?一个有效的数据人员应该对项目如何完成有一个好的想法。每个项目的最佳实践可能有所不同。
无论是仪表板、模型、数据产品还是电子表格文件;最好的输出是什么,怎么做?
“从心中的目标开始,意味着从清楚地了解你的目的地开始。这意味着知道你要去哪里,这样你才能更好地了解你现在在哪里,这样你迈出的步伐才会始终朝着正确的方向。”—斯蒂芬·柯维
3-把重要的事情放在第一位
艾森豪威尔决策矩阵。图片作者。
数据科学不在于你做了多少分析。
必须优先解决最重要和最紧迫的问题。如果数据科学团队被大量的请求所淹没,这 4 个象限必须明确。一个有效的数据人员知道应该先做什么。我们为什么要在无关紧要的事情上费心呢?
衡量什么是重要的,什么是紧急的,并始终坚持最高优先级从长远来看会有回报。
"关键不在于优先处理你日程表上的事情,而在于安排好你的优先事项."—斯蒂芬·柯维
4——考虑双赢
数据科学与你的工作表现无关。
你有没有遇到过阻碍,想要用你的方式解决?伟大的组织在内部(甚至外部)协作。思考双方如何双赢是获得信任和确保长期成功的关键。一个有效的数据家伙知道每个人都可以赢!如果一个人赢不了,再想想。
关注那些不是片面的甚至只是权宜之计的解决方案。
“这不是你的方式或我的方式;是更好的方式,更高的方式。”—斯蒂芬·柯维
5 —首先寻求理解,然后寻求被理解
照片由 Devin Avery 在 Unsplash 上拍摄
数据科学不在于你的项目有多复杂。
大多数数据人认为数字和逻辑驱动,而其他大多数人不这么认为。然而,并不是所有的问题都可以用数字来解释。获得对人的充分理解的关键是同理心和开放的心态。一个有效的数据人员在做出假设之前应该首先理解。错误的方法不太有效。
寻求另一种观点,或许会开启另一种更好的可能性。让我们多听听!
“大多数人没有带着理解的意图去听;他们听着,意图回答。”—斯蒂芬·柯维
6 —协同增效
佩里·格罗内在 Unsplash 上的照片
数据科学不是你一个人的工作。
要完美地执行一个精心设计的计划,团队合作是必不可少的。想象一下:没有实现的数据建模;没有用户定义的指标的仪表板;没有数据工程的分析。没用。一个高效的数据人员必须能够与其他人同步工作。组织之所以存在,是因为每个人都在其中。
求协作!每个人可能会提供不同的和更好的选择。
“协同优于我的方式或你的方式。这是我们的方式。”—斯蒂芬·柯维
7 —磨快锯子
我意识到,数据科学是巨大的。
只有通过持续不断的学习,才能对数据科学有更深入的了解。争取更多的知识,适应新的问题。没有放之四海而皆准的方法。一个高效的数据人员随时准备解决任何问题。发展自己,接受挑战!
记住你是你最大的财富!
"除非你不断提高自己的技能,否则你很快就会变得无关紧要。"—斯蒂芬·柯维
现在,你最需要改进的习惯是什么?
我知道这些习惯已经有 5 年了,但是我还在继续努力。控制习惯很难,因为它们迫使你走出舒适区。但是,不要担心你不能控制的事情,记住你只能控制你自己。
“从小事做起,许下承诺并信守承诺。然后,许下更大的承诺,并信守承诺。”—斯蒂芬·柯维
更进一步,实际上还有第八个习惯。找到自己的声音,并激励他人找到自己的声音。话题从效率转移到伟大。将愿景、纪律、激情和良知转化为灵感。如果你是数据科学的领导者,请考虑阅读它!
一步一个脚印!祝大家平安健康。
参考
[1]斯蒂芬·R·科维,《高效能人士的 7 个习惯 (1989)
[2]斯蒂芬·R·科维,第八个习惯:从有效到伟大 (2004)