1. 機(jī)器學(xué)習(xí)常用損失函數(shù)
評(píng)價(jià)模型預(yù)測(cè)值和真實(shí)值的函數(shù)為損失函數(shù)(loss function)。它是一個(gè)非負(fù)實(shí)值函數(shù)壳嚎,損失函數(shù)越小,模型的性能就越好末早。
模型最終需要優(yōu)化函數(shù)的是代價(jià)函數(shù)(cost function)或者說目標(biāo)函數(shù)烟馅,需要在損失函數(shù)的基礎(chǔ)上做一些調(diào)整,例如正則化然磷。
下面主要列出幾種常見的損失函數(shù)郑趁。
1.1. log對(duì)數(shù)損失函數(shù)(邏輯回歸損失,交叉熵?fù)p失)
有些人可能覺得邏輯回歸的損失函數(shù)就是平方損失姿搜,其實(shí)并不是寡润。平方損失函數(shù)可以通過線性回歸在假設(shè)樣本是高斯分布的條件下推導(dǎo)得到,而邏輯回歸得到的并不是平方損失舅柜。在邏輯回歸的推導(dǎo)中梭纹,它假設(shè)樣本服從伯努利分布(0-1分布),然后求得滿足該分布的似然函數(shù)致份,接著取對(duì)數(shù)求極值等等变抽。而邏輯回歸并沒有求似然函數(shù)的極值,而是把極大化當(dāng)做是一種思想氮块,進(jìn)而推導(dǎo)出它的經(jīng)驗(yàn)風(fēng)險(xiǎn)函數(shù)為:最小化負(fù)的似然函數(shù)(即max F(y, f(x)) —> min -F(y, f(x)))绍载。從損失函數(shù)的視角來看,它就成了log損失函數(shù)了滔蝉。
log損失函數(shù)的標(biāo)準(zhǔn)形式:
剛剛說到击儡,取對(duì)數(shù)是為了方便計(jì)算極大似然估計(jì),因?yàn)樵贛LE中蝠引,直接求導(dǎo)比較困難阳谍,所以通常都是先取對(duì)數(shù)再求導(dǎo)找極值點(diǎn)。損失函數(shù)L(Y, P(Y|X))表達(dá)的是樣本X在分類Y的情況下螃概,使概率P(Y|X)達(dá)到最大值(換言之边坤,就是利用已知的樣本分布,找到最有可能(即最大概率)導(dǎo)致這種分布的參數(shù)值谅年;或者說什么樣的參數(shù)才能使我們觀測(cè)到目前這組數(shù)據(jù)的概率最大)茧痒。因?yàn)閘og函數(shù)是單調(diào)遞增的,所以logP(Y|X)也會(huì)達(dá)到最大值融蹂,因此在前面加上負(fù)號(hào)之后旺订,最大化P(Y|X)就等價(jià)于最小化L了弄企。
邏輯回歸的P(Y=y|x)表達(dá)式如下(為了將類別標(biāo)簽y統(tǒng)一為1和0,下面將表達(dá)式分開表示)
將它帶入到上式区拳,通過推導(dǎo)可以得到logistic的損失函數(shù)表達(dá)式拘领,如下:
邏輯回歸最后得到的目標(biāo)式子如下:
上面是針對(duì)二分類而言的。這里需要解釋一下:之所以有人認(rèn)為邏輯回歸是平方損失樱调,是因?yàn)樵谑褂锰荻认陆祦砬笞顑?yōu)解的時(shí)候约素,它的迭代式子與平方損失求導(dǎo)后的式子非常相似,從而給人一種直觀上的錯(cuò)覺笆凌。
1.2. 平方損失函數(shù)(最小二乘法, Ordinary Least Squares )
最小二乘法是線性回歸的一種圣猎,OLS將問題轉(zhuǎn)化成了一個(gè)凸優(yōu)化問題。在線性回歸中乞而,它假設(shè)樣本和噪聲都服從高斯分布(為什么假設(shè)成高斯分布呢送悔?其實(shí)這里隱藏了一個(gè)小知識(shí)點(diǎn),就是中心極限定理爪模,可以參考【central limit theorem】)欠啤,最后通過極大似然估計(jì)(MLE)可以推導(dǎo)出最小二乘式子。最小二乘的基本原則是:最優(yōu)擬合直線應(yīng)該是使各點(diǎn)到回歸直線的距離和最小的直線屋灌,即平方和最小洁段。換言之,OLS是基于距離的共郭,而這個(gè)距離就是我們用的最多的歐幾里得距離眉撵。為什么它會(huì)選擇使用歐式距離作為誤差度量呢(即Mean squared error, MSE)落塑,主要有以下幾個(gè)原因:
簡(jiǎn)單纽疟,計(jì)算方便;
歐氏距離是一種很好的相似性度量標(biāo)準(zhǔn)憾赁;
在不同的表示域變換后特征性質(zhì)不變污朽。
平方損失(Square loss)的標(biāo)準(zhǔn)形式如下:
當(dāng)樣本個(gè)數(shù)為n時(shí),此時(shí)的損失函數(shù)變?yōu)椋?/p>
Y-f(X)表示的是殘差龙考,整個(gè)式子表示的是殘差的平方和蟆肆,而我們的目的就是最小化這個(gè)目標(biāo)函數(shù)值(注:該式子未加入正則項(xiàng)),也就是最小化殘差的平方和(residual sum of squares晦款,RSS)炎功。
而在實(shí)際應(yīng)用中,通常會(huì)使用均方差(MSE)作為一項(xiàng)衡量指標(biāo)缓溅,公式如下:
上面提到了線性回歸蛇损,這里額外補(bǔ)充一句,我們通常說的線性有兩種情況,一種是因變量y是自變量x的線性函數(shù)淤齐,一種是因變量y是參數(shù)α的線性函數(shù)股囊。在機(jī)器學(xué)習(xí)中,通常指的都是后一種情況更啄。
1.3.指數(shù)損失函數(shù)(Adaboost)
學(xué)過Adaboost算法的人都知道稚疹,它是前向分步加法算法的特例,是一個(gè)加和模型祭务,損失函數(shù)就是指數(shù)函數(shù)内狗。在Adaboost中,經(jīng)過m此迭代之后义锥,可以得到fm(x):
Adaboost每次迭代時(shí)的目的是為了找到最小化下列式子時(shí)的參數(shù)α 和G:
而指數(shù)損失函數(shù)(exp-loss)的標(biāo)準(zhǔn)形式如下
可以看出柳沙,Adaboost的目標(biāo)式子就是指數(shù)損失,在給定n個(gè)樣本的情況下缨该,Adaboost的損失函數(shù)為:
1.4.Hinge損失函數(shù)(SVM)
在機(jī)器學(xué)習(xí)算法中偎行,hinge損失函數(shù)和SVM是息息相關(guān)的川背。在線性支持向量機(jī)中贰拿,最優(yōu)化問題可以等價(jià)于下列式子:
下面來對(duì)式子做個(gè)變形,令:
于是熄云,原式就變成了:
如若取λ=1/2C膨更,式子就可以表示成:
可以看出,該式子與下式非常相似:
前半部分中的l就是hinge損失函數(shù)缴允,而后面相當(dāng)于L2正則項(xiàng)荚守。
Hinge 損失函數(shù)的標(biāo)準(zhǔn)形式
可以看出,當(dāng)|y|>=1時(shí)练般,L(y)=0矗漾。
1.5. 其它損失函數(shù)
除了以上這幾種損失函數(shù),常用的還有:
0-1損失函數(shù)
絕對(duì)值損失函數(shù)
Python
先記錄一下python中數(shù)據(jù)結(jié)構(gòu)字典的排序方法薄料。
字典以鍵或值排序:
>>> dict1={"Beijing":2, "Shanghai":3, "Guangzhou":1}
>>> sorted(dict1.items(), key=lambda A:A[0])
#在python2.7中第一個(gè)參數(shù)需要改為dict1.iteritems()
[('Beijing', 2), ('Guangzhou', 1), ('Shanghai', 3)]
>>> sorted(dict1.items(), key=lambda A:A[1])
[('Guangzhou', 1), ('Beijing', 2), ('Shanghai', 3)]
如果要以從大到小進(jìn)行排序敞贡,只需要加上reverse=True參數(shù)即可。
>>> sorted(dict1.items(), key=lambda A:A[0], reverse=True)
[('Shanghai', 3), ('Guangzhou', 1), ('Beijing', 2)]
>>> sorted(dict1.items(), key=lambda A:A[1], reverse=True)
[('Shanghai', 3), ('Beijing', 2), ('Guangzhou', 1)]
注意:sorted()的第一個(gè)參數(shù)為iterable摄职。因?yàn)樽值浔旧聿⒉皇莍terable的誊役,需要利用items()函數(shù)將字典轉(zhuǎn)換為可迭代的。
冒泡排序
#89,45,68,90,29,34,17
#倒序排列
list1 = [89,45,68,90,29,34,17]
for i in range(len(list1)):
for j in range(len(list1)-1-i):
if list1[j + 1] < list1[j]:
list1[j],list1[j + 1] = list1[j + 1],list1[j]
#正序排列
def bubble_sort(lists):
# 冒泡排序
count = len(lists)
for i in range(0, count):
for j in range(i + 1, count):
if lists[i] > lists[j]:
lists[i], lists[j] = lists[j], lists[i]
return lists
插入排序
描述:
插入排序的基本操作就是將一個(gè)數(shù)據(jù)插入到已經(jīng)排好序的有序數(shù)據(jù)中谷市,從而得到一個(gè)新的蛔垢、個(gè)數(shù)加一的有序數(shù)據(jù),算法適用于少量數(shù)據(jù)的排序迫悠,時(shí)間復(fù)雜度為O(n^2)鹏漆。是穩(wěn)定的排序方法。
插入算法把要排序的數(shù)組分成兩部分:第一部分包含了這個(gè)數(shù)組的所有元素,但將最后一個(gè)元素除外(讓數(shù)組多一個(gè)空間才有插入的位置)甫男,而第二部分就只包含這一個(gè)元素(即待插入元素)且改。在第一部分排序完成后,再將這個(gè)最后元素插入到已排好序的第一部分中板驳。
list1 = [89,45,68,90,29,34,17]
for i in range(1, len(list1)):
key = list1[i]
j = i - 1
while j >= 0:
if list1[j] > key:
list1[j + 1] = list1[j]
list1[j] = key
j = j - 1
print (list1)
#算法課本P104頁例子
#key的左邊都是排好的又跛,插入排序就是把key插入到左邊排好的序列中去
#因?yàn)樽筮呉呀?jīng)排好了,所以如果比最后一個(gè)元素大就只需要和最后一個(gè)元素比就行
#否則key向前移動(dòng)若治,直到比較完前面所有的值
#一個(gè)key插入完以后慨蓝,key自增,直到右邊沒有元素為止
def Insert_sort(list1):
count = len(list1)
for i in range(1,count): #假設(shè)第一個(gè)數(shù)是排序好的
key = list1[i] #取出當(dāng)前未排序的數(shù)
j = i - 1 #從后往前端幼,先取未排序數(shù)的前一個(gè)數(shù)(已經(jīng)排序好的數(shù))
while j >= 0 and list1[j] > key:#若當(dāng)前未排序的數(shù)比排序好的數(shù)還小礼烈,并沒有到數(shù)組的開頭。
list1[j+1] = list1[j] #排序好的數(shù)往后挪一個(gè)位置
j = j - 1 #去排序好的數(shù)的前一個(gè)數(shù)進(jìn)行比較
list1[j+1] = key #插入當(dāng)前要排序的數(shù)
return list1
希爾排序
希爾排序(Shell Sort)是插入排序的一種婆跑。
也稱縮小增量排序此熬,是直接插入排序算法的一種更高效的改進(jìn)版本。希爾排序是非穩(wěn)定排序算法滑进。該方法因DL.Shell于1959年提出而得名犀忱。 同時(shí)該算法是沖破O(n^2)的第一批算法之一。
希爾排序是把記錄按下標(biāo)的一定增量分組扶关,對(duì)每組使用直接插入排序算法排序阴汇;推薦的增量是 length/2,雖然他不一定是最好的。
隨著增量逐漸減少节槐,每組包含的關(guān)鍵詞越來越多搀庶,當(dāng)增量減至1時(shí),整個(gè)文件恰被分成一組铜异,算法便終止哥倔。
list1 = [89,45,68,90,29,34,17]
count = len(list1)
step = 2
group = count // step
while group > 0:
for i in range(0, group):
j = i + group
while j < count:
k = j - group
key = list1[j]
while k >= 0:
if list1[k] > key:
list1[k + group] = list1[k]
list1[k] = key
k -= group
j += group
group = group // step
print (list1)
#python2中/代表整除,python3中//代表整除揍庄,/代表小數(shù)除
#list1 = [89,45,68,90,29,34,17]
def shell_sort(lists):
# 希爾排序
count = len(lists)
step = 2
group = count // step
while group > 0:
for i in range(0, group):
j = i + group
while j < count:
k = j - group
key = lists[j]
while k >= 0:
if lists[k] > key:
lists[k + group] = lists[k]
lists[k] = key
k -= group
j += group
group = group // step
return lists
選擇排序
描述:
基本思想:第1次比較咆蒿,在待排序記錄r1 ~ r[n]中選出最小的記錄,將它與r1交換币绩;第2次比較蜡秽,在待排序記錄r2 ~ r[n]中選出最小的記錄,將它與r2交換缆镣;以此類推芽突,第i次在待排序記錄r[i] ~ r[n]中選出最小的記錄,將它與r[i]交換董瞻,使有序序列不斷增長(zhǎng)直到全部排序完畢寞蚌,對(duì)初始排列不敏感田巴,無論怎樣都要比較n-1次。
list1 = [89,45,68,90,29,34,17]
count = len(list1)
for i in range(count):
min = i
for j in range(i + 1, count):
if list1[min] > list1[j]:
min == j
list1[min],list1[j] = list1[j],list1[min]
print (list1)
def select_sort(lists):
# 選擇排序
count = len(lists)
for i in range(0, count):
min = i
for j in range(i + 1, count):
if lists[min] > lists[j]:
min = j
lists[min], lists[i] = lists[i], lists[min]
return lists
堆排序
一個(gè)完全二叉樹大根堆需要滿足:
i 為下標(biāo)挟秤,n為元素個(gè)數(shù)壹哺,i是從1開始的,0位置需要使用占位符
流程:
1.首先構(gòu)造堆
2.取出這個(gè)大根堆的堆頂節(jié)點(diǎn)(最大值)艘刚,與堆的最下最右的元素進(jìn)行交換管宵,然后把剩下的元素再構(gòu)造一個(gè)大根堆
3.重復(fù)第二步,直到這個(gè)大根堆的長(zhǎng)度為1攀甚,此時(shí)完成排序箩朴。
#占位符比較麻煩,直接使用python中的鏈表結(jié)構(gòu)
from collections import deque
def swap_param(L, i, j):
L[i], L[j] = L[j], L[i]
return L
def heap_adjust(L, start, end):
temp = L[start]
i = start
j = 2 * i
while j <= end:
if (j < end) and (L[j] < L[j + 1]):
j += 1
if temp < L[j]:
L[i] = L[j]
i = j
j = 2 * i
else:
break
L[i] = temp
def heap_sort(L):
L_length = len(L) - 1
first_sort_count = L_length // 2
for i in range(first_sort_count):
heap_adjust(L, first_sort_count - i, L_length)
for i in range(L_length - 1):
L = swap_param(L, 1, L_length - i)
heap_adjust(L, 1, L_length - i - 1)
return [L[i] for i in range(1, len(L))]
def main():
L = deque([50, 16, 30, 10, 60, 90, 2, 80, 70])
L.appendleft(0)
print (heap_sort(L))
if __name__ == '__main__':
main()
描述:
堆排序(Heapsort)是指利用堆積樹(堆)這種數(shù)據(jù)結(jié)構(gòu)所設(shè)計(jì)的一種排序算法秋度,它是選擇排序的一種炸庞。可以利用數(shù)組的特點(diǎn)快速定位指定索引的元素荚斯。堆分為大根堆和小根堆埠居,是完全二叉樹。大根堆的要求是每個(gè)節(jié)點(diǎn)的值都不大于其父節(jié)點(diǎn)的值事期,即A[PARENT[i]] >= A[i]滥壕。在數(shù)組的非降序排序中,需要使用的就是大根堆刑赶,因?yàn)楦鶕?jù)大根堆的要求可知捏浊,最大的值一定在堆頂懂衩。
遞歸構(gòu)造堆撞叨,偽代碼:
MAX-HEAPIFY(A, i)
1 l ← LEFT(i)
2 r ← RIGHT(i)
3 if l ≤ heap-size[A] and A[l] > A[i]
4 then largest ← l
5 else largest ← i
6 if r ≤ heap-size[A] and A[r] > A[largest]
7 then largest ← r
8 if largest ≠ i
9 then exchange A[i] <-> A[largest]
10 MAX-HEAPIFY(A, largest)
快速排序
一趟快速排序的算法是:
1)設(shè)置兩個(gè)變量i、j浊洞,排序開始的時(shí)候:i=0牵敷,j=N-1;
2)以第一個(gè)數(shù)組元素作為關(guān)鍵數(shù)據(jù)法希,賦值給key枷餐,即key=A[0];
3)從j開始向前搜索苫亦,即由后開始向前搜索(j--)毛肋,找到第一個(gè)小于key的值A(chǔ)[j],將A[j]和A[i]互換屋剑;
4)從i開始向后搜索润匙,即由前開始向后搜索(i++),找到第一個(gè)大于key的A[i]唉匾,將A[i]和A[j]互換孕讳;
5)重復(fù)第3匠楚、4步,直到i=j厂财; (3,4步中芋簿,沒找到符合條件的值,即3中A[j]不小于key,4中A[i]不大于key的時(shí)候改變j璃饱、i的值与斤,使得j=j-1,i=i+1荚恶,直至找到為止幽告。找到符合條件的值,進(jìn)行交換的時(shí)候i裆甩, j指針位置不變冗锁。另外,i==j這一過程一定正好是i+或j-完成的時(shí)候嗤栓,此時(shí)令循環(huán)結(jié)束)冻河。
算法導(dǎo)論中的版本
def quick_sort(array, l, r):
if l < r:
q = partition(array, l, r)
quick_sort(array, l, q - 1)
quick_sort(array, q + 1, r)
def partition(array, l, r):
x = array[r]
i = l - 1
for j in range(l, r):
if array[j] <= x:
i += 1
array[i], array[j] = array[j], array[i]
array[i + 1], array[r] = array[r], array[i+1]
return i + 1