新书下载 | 面向机器学习的数学(Mathematics for Machine Learning)

《面向机器学习的数学(Mathematics for Machine Learning)》是一本新书,由伦敦帝国理工学院的几位教授出版,于2020年出版,该书pdf电子版是完全公开的,在该书主页可以直接下载:https://mml-book.github.io/

该书电子版总计有16M,400多页,需要的同学也可以关注下方公众号,后台回复 MML 获取百度网盘地址:

Coursera上也有同名配套课程,可以同步参考:伦敦帝国理工学院的面向机器学习的数学专项课程系列(Mathematics for Machine Learning Specialization),该系列包含3门子课程,涵盖线性代数,多变量微积分,以及主成分分析(PCA),这个专项系列课程的目标是弥补数学与机器学习以及数据科学鸿沟,感兴趣的同学可以关注:Mathematics for Machine Learning。Learn about the prerequisite mathematics for applications in data science and machine learning。

该书主要包括两部分内容:数学基础和机器学习核心问题

资料获取方式:该书官网或者关注下方公众号,回复 MML 即可获取该书最新版PDF百度网盘地址:

强化学习圣经:《强化学习导论》第二版(附PDF下载)

今天推荐 Richard S. Sutton 教授与 Andrew G. Barto 教授合著的《强化学习导论(第二版)》, Richard S. Sutton 就职于iCORE大学计算机科学系,是强化学习领域的专家,其在强化学习领域的著作“Reinforcement Learning”一直是认为是强化学习方面的圣经,本书官网为:

http://www.incompleteideas.net/book/the-book.html

可以在官网直接下载该书第二版PDF电子版及相关资料,总计有80多M,548页。

需要的同学也可以按照以下方式获取,同时包括该书的其他相关资料:

​关注下方公众号,后台回复 RLBook 即可获取百度网盘地址:

该书有一份对应的 Python 代码实现,感兴趣的同学可以参考(GitHub链接):

https://github.com/ShangtongZhang/reinforcement-learning-an-introduction

作者介绍

Richard S. Sutton是加拿大的一个计算机科学家,当前任职于iCORE大学计算机科学系。Sutton是强化学习领域巨擘,在temporal difference learning, policy gradient methods, the Dyna architecture等方面都有重大贡献。自2003年起,Sutton就出任iCORE大学计算机科学系的教授,在这里他领导了强化学习和人工智能实验室(RLAI)。

https://www.ualberta.ca/science/about-us/contact-us/faculty-directory/rich-sutton

Andrew Barto 是Massachusetts大学Amherst分校的教授, 已于2012年退休.

退休前, 他是Massachusetts大学Amherst分校自治学习实验室主任.

目前, 他是Massachusetts大学神经科学和行为项目的准会员, Neural Computation 副主编, Machine Learning Research杂志顾问,  Adaptive Behavior的编辑.

Barto教授是美国科学促进会会员,IEEE Fellow, 以及神经科学学会会员.

他因强化学习领域的贡献而获得2004年IEEE神经网络协会先锋奖, IJCAI-17杰出研究奖.

他在期刊,书籍,会议和研讨会中发表论文一百多篇。他与Richard Sutton共同编写了 "Reinforcement Learning: An Introduction," MIT Press 1998,迄今已收到超过25,000次引用。本书的第二版已发布。

履历:

Massachusetts大学的计算机科学系主任----- 2007-2011年

Massachusetts大学的计算机科学系教授----- 1991年

Massachusetts大学的计算机科学系副教授----- 1982年

Massachusetts大学的计算机科学系博士后----- 1977年

获Michigan大学数学专业学士学位----- 1970年

获Michigan大学计算机科学专业博士学位----- 1975年

http://www-all.cs.umass.edu/~barto/

资料获取方式,该书官网或者关注下方公众号,回复 RLbook 即可获取该书最新版PDF百度网盘地址:

自然语言处理经典书籍《Speech and Language Processing》第三版最新版下载(含第二版)

自然语言处理领域的经典图书《Speech and Language Processing-An Introduction to Natural Language Processing, Computational Linguistics, and Speech Recognition》,中文译名《自然语言处理综论》,目前第三版正在撰写修订中,这本书的作者是NLP领域的大神 Daniel Jurafsky 教授和 James H. Martin 教授,原定2019年年底完工,不过目前还没有完成,但是大部分章节目前已经有草稿了,截止目前发布了一个截止2019年10月23日的单pdf文件,621页,可以在该书官网直接获取:https://web.stanford.edu/~jurafsky/slp3/

需要的同学也可以按照以下方式获取,同时包括该书第二版英文版PDF:

关注下方公众号,后台回复SLPD即可获取百度网盘地址:

这本书英文版第一版自2000年出版,第二版英文版2008年出版,至今跨越接近20年,特别是这几年深度学习的风生水起,第三版增加了很多NLP和深度学习相关的内容,相对第二版变化有些大,这个第三版已完成章节的电子版草稿,总计有621页,估计全书完成时要秒杀第二版的厚度。

与上个草稿版本相比,新增了第10、22、23、27章节,着重重写了第9、19和26章节,并根据读者的建议对其他章节做了一些修正。

“This fall's updates so far include new chapters 10, 22, 23, 27, significantly rewritten versions of Chapters 9, 19, and 26, and a pass on all the other chapters with modern updates and fixes for the many typos and suggestions from you our loyal readers!”

关于作者,两位都是NLP领域的神牛,以下是第二版中文翻译版中关于作者的介绍:

Daniel Jurafsky现任斯坦福大学语言学系和计算机科学系副教授。在此之前,他曾在博尔德的科罗拉多大学语言学系、计算机科学系和认知科学研究所任职。他出生于纽约州的Yonkers,1983年获语言学学士,1992年获计算机科学博士,两个学位都在伯克利加利福尼亚大学获得。他于1998年获得美国国家基金会CAREER奖,2002年获得Mac-Arthur奖。他发表过90多篇论文,内容涉及语音和语音处理的广泛领域。James H. Martin现任博尔德的科罗拉多大学语言学系、计算机科学系教授,认知科学研究所研究员。他出生于纽约市,1981年获可伦比亚大学计算机科学学士,1988年获伯克利加利福尼亚大学计算机科学博士。他写过70多篇关于计算机科学的论著,出版过《隐喻解释的计算机模型》(A Computational Model of Metaphor Interpretation)一书。

资料获取方式,该书官网或者关注下方公众号,回复SLPD即可获取该书最新版PDF百度网盘地址:

Python Tip 3: Python lambda表达式

Python中的Lambda表达式很可能让人迷惑,我们还是从一段官方解释文字开始:

Small anonymous functions can be created with the lambda keyword. This function returns the sum of its two arguments: lambda a, b: a+b. Lambda functions can be used wherever function objects are required. They are syntactically restricted to a single expression. Semantically, they are just syntactic sugar for a normal function definition.

理解lambda表达式的本质需要回答两个问题:为什么需要lambda?lambda的好处在哪里?

首先为什么需要lambda? 让我们看几个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
>>> add_one = lambda x: x + 1
>>> add_one(1)
2
>>> add_one(2)
3
>>> add_one(100)
101
>>> def add_one_fun(x):
...     return x + 1
... 
>>> add_one_fun(1)
2
>>> add_one_fun(100)
101
>>> x_sum_y = lambda x, y: x + y
>>> x_sum_y(1, 1)
2
>>> x_sum_y(1, 10)
11
>>> def sum_fun(x, y):
...     return x + y
... 
>>> sum_fun(1, 1)
2
>>> sum_fun(1, 2)
3
>>> sum_fun(1, 10)
11

很遗憾,lambda并非必须,每个lambda都可以用函数替代。那么我们为什么需要lambda表达式?或者lambda表达式有什么优点?其实,官方文档里已经说得很明确了:首先,它是一个小的匿名函数,可以用在任何需要函数对象的地方,它的语法被严格限制在一行表达式(并且不应该太复杂);从语义上来讲,它们只是正常的函数定义的语法糖。

Python不是一个纯函数式编程语言,但是引入了sorted, map, reduce, filter等函数式编程的一些特性, 可以将函数作为参数传给这些函数,这里,lambda表达式就派上用场了。我们熟悉的大概是sorted函数了:

1
2
3
4
5
>>> test_list = [('two', 2), ('three', 3), ('one', 1), ('five', 5), ('four', 4)]
>>> sorted(test_list, key=lambda x: x[1])
[('one', 1), ('two', 2), ('three', 3), ('four', 4), ('five', 5)]
>>> sorted(test_list, key=lambda x: x[0])
[('five', 5), ('four', 4), ('one', 1), ('three', 3), ('two', 2)]

map() 接收一个 function 和一个 iterable 作为参数,该函数会返回一个迭代器,该迭代器将 function 应用于 iterable 的所有元素,并产生结果。下述示例,对列表中所有元素求平方和立方:

1
2
3
4
5
6
7
>>> test_list = [2, 3, 5, 7, 11, 13, 19, 17]
>>> map_list = list(map(lambda x: x ** 2, test_list))
>>> map_list
[4, 9, 25, 49, 121, 169, 361, 289]
>>> map_list = list(map(lambda x: x ** 3, test_list))
>>> map_list
[8, 27, 125, 343, 1331, 2197, 6859, 4913]

reduce() 函数会对参数序列中元素进行累积。函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。注意在python3中reduce已经从全局函数中移除,需要从functools中import,我们来看一个1到100整数求和的例子:

1
2
3
4
5
6
>>> test_list = list(range(1, 101))
>>> test_list
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
>>> from functools import reduce
>>> reduce(lambda x, y: x + y, test_list)
5050

filter() 提供了一种优雅的方法来过滤 iterable(可迭代对象,例如:列表)中的所有元素,过滤条件是 function 返回 True:

1
2
3
4
5
6
7
>>> test_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
>>> filter_list = list(filter(lambda x: x % 3 == 0, test_list))
>>> filter_list
[3, 6, 9, 12, 15]
>>> filter_list = list(filter(lambda x: x % 5 == 0, test_list))
>>> filter_list
[5, 10, 15]

注:原创文章,转载请注明出处及保留链接“Python时代”:http://www.pythonage.com/

本文链接地址:Python Tip 3: Python lambda表达式 http://www.pythonage.com/?p=47

Python Tip 2: Python *args **kwargs 用法

关于Python中 *args 和 **kwargs的用法,网上有很多介绍文章,还是先看一段官方的解释:

When a final formal parameter of the form **name is present, it receives a dictionary (see Mapping Types — dict) containing all keyword arguments except for those corresponding to a formal parameter. This may be combined with a formal parameter of the form *name (described in the next subsection) which receives a tuple containing the positional arguments beyond the formal parameter list. (*name must occur before **name.)

首先需要了解Python函数传递参数的方式有两种:

位置参数(positional argument)
关键词参数(keyword argument)

然后再来看*args(*name)与**kwargs(**name)的区别,两者都是python中的可变参数:

*args表示任何多个无名参数,它本质是一个tuple;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
>>> def args_one(narg, *args):
...     print("Normal arg:", narg)
...     for arg in args:
...         print("other args:", arg)
... 
>>> args_one(1, 2, 3, 4, 5)
Normal arg: 1
other args: 2
other args: 3
other args: 4
other args: 5
>>> args_one('a', 'b', 'c', 'd', 'e')
Normal arg: a
other args: b
other args: c
other args: d
other args: e
>>> tuple_args = (1, 2, 3, 4, 5)
>>> args_one(1, tuple_args)
Normal arg: 1
other args: (1, 2, 3, 4, 5)
>>> args_one(1, *tuple_args)
Normal arg: 1
other args: 1
other args: 2
other args: 3
other args: 4
other args: 5

**kwargs表示关键字参数,它本质上是一个dict:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
>>> def args_two(narg, **kwargs):
...     print("Normal arg:", narg)
...     for key, value in kwargs.items():
...         print("other keyword arg: %s: %s" % (key, value))
... 
>>> args_two(narg=1, karg1="one", karg2="two")
Normal arg: 1
other keyword arg: karg1: one
other keyword arg: karg2: two
>>> key_args = {"a": "A", "b": "B", "c": "C"}
# 以下两个是错误的调用示范
>>> args_two('a', key_args)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: args_two() takes 1 positional argument but 2 were given
>>> args_two('a', *key_args)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: args_two() takes 1 positional argument but 4 were given
# 正确做法
>>> args_two('a', **key_args)
Normal arg: a
other keyword arg: a: A
other keyword arg: b: B
other keyword arg: c: C

同时使用*args和**kwargs时,必须*args参数列要在**kwargs前, 让我们最后来看一个综合例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
>>> def args_fun(normal, *args, **kwargs):
...     print("narmal=", normal)
...     print("*args=", args)
...     print("**args=", kwargs)
... 
>>> args_fun(1)
narmal= 1
*args= ()
**args= {}
>>> args_fun(1, 2)
narmal= 1
*args= (2,)
**args= {}
>>> args_fun(1, 2, 3)
narmal= 1
*args= (2, 3)
**args= {}
>>> args_fun(1, a=1, b=2, c=3)
narmal= 1
*args= ()
**args= {'a': 1, 'b': 2, 'c': 3}
>>> args_fun(1, 2, 3, a=1, b=2, c=3)
narmal= 1
*args= (2, 3)
**args= {'a': 1, 'b': 2, 'c': 3}
>>> args_fun(1, a=1, b=2, c=3, 2, 3)
  File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument

注:原创文章,转载请注明出处及保留链接“Python时代”:http://www.pythonage.com/

本文链接地址:Python Tip 2: python *args **kwargs 用法 http://www.pythonage.com/?p=15

Python Tip 1:Python循环语句中的else语法(for else, while else)

在Python的学习过程中,for...else..., while...else...语法可能是比较令人困惑的一个Python知识点。先来看一段Python官方的说明:

"Loop statements may have an else clause; it is executed when the loop terminates through exhaustion of the list (with for) or when the condition becomes false (with while), but not when the loop is terminated by a break statement."

Python循环语句(for, while)有可能带一个else分支,当一个for循环正常执行完毕时或者当一个while循环正常执行完毕(循环条件变为false)时它被触发执行,但是如果这个循环被break语句非正常中止时,则这个else分支不执行。让我们来看几个例子:

1)for循环正常执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> for i in range(7):
...     print(i)
... else:
...     print("No abnormal interruption")
... 
0
1
2
3
4
5
6
No abnormal interruption

2) for循环“非正常”break中断:

1
2
3
4
5
6
7
8
9
10
11
>>> for i in range(7):
...     print(i)
...     if i == 3:
...         break
... else:
...     print("No abnormal interruption")
... 
0
1
2
3

3) while循环正常执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> i = 0
>>> while i < 7:
...     print(i)
...     i += 1
... else:
...     print("No abnormal interruption")
... 
0
1
2
3
4
5
6
No abnormal interruption

4) while循环“非正常”break中断:

1
2
3
4
5
6
7
8
9
10
11
12
>>> i = 0
>>> while i < 7:
...     print(i)
...     i += 1
...     if i == 3:
...         break
... else:
...     print("No abnormal interruption")
... 
0
1
2

总结起来就是:当循环中没有break时,else被执行,当循环中有break并且被触发时,else不被执行,类似这个case,即使循环语句中有break,但是如果没有触发break正常执行完毕,也会执行else:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> for i in range(7):
...     print(i)
...     if i > 7:
...         break
... else:
...     print("No abnormal interruption")
... 
0
1
2
3
4
5
6
No abnormal interruption

那么,这个有点绕口的Python语法糖有什么好处呢?让我们再看一个Case,这个Case参考自Python官方的一个寻找素数的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
>>> for n in range(2, 20):
...     for x in range(2, n):
...         if n % x == 0:
...            print(n, 'equals', x, '*', n//x)
...            break
...     else:
...         print(n, 'is a prime number')
... 
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3
10 equals 2 * 5
11 is a prime number
12 equals 2 * 6
13 is a prime number
14 equals 2 * 7
15 equals 3 * 5
16 equals 2 * 8
17 is a prime number
18 equals 2 * 9
19 is a prime number

想一想,如果没有else语句,如何实现?我是这样实现的,加一个flag标志来判断:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
>>> for n in range(2, 20):
...     flag = 0
...     for x in range(2, n):
...         if n % x == 0:
...             print(n, 'equals', x, '*', n//x)
...             flag = 1
...             break
...     if flag == 0:
...         print(n, 'is a prime number')
... 
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3
10 equals 2 * 5
11 is a prime number
12 equals 2 * 6
13 is a prime number
14 equals 2 * 7
15 equals 3 * 5
16 equals 2 * 8
17 is a prime number
18 equals 2 * 9
19 is a prime number

这样看来,是不是觉得前者更 pythonic 一些?

注:原创文章,转载请注明出处及保留链接“Python时代”:http://www.pythonage.com/

本文链接地址:Python Tip 1:Python循环语句中的else语法(for else, while else)

The Zen of Python

The Zen of Python

by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!