Precision就是PPV
Recall就是Sensetivity
只是統計與ML的習慣用詞不同
另外F1 score是Precision跟Recall的調和平均。
知識是人類最大資產
Precision就是PPV
Recall就是Sensetivity
只是統計與ML的習慣用詞不同
另外F1 score是Precision跟Recall的調和平均。
這是一本教你如何成為軟體技術主管的書籍,從一開始告訴你身處非主管身份要如何被管理講起,再到小主管最後成為高階主管以及如何塑造公司文化。
其中某些部份在不同職級都會被一再強調,最重要的我想就是「一對一會談」。作者非常推崇主管要與成員一周一次會談,會談不應該是單純閒聊或是進度報告。作者認為主管應該要基本了解成員的生活狀況,至少他們在生活上碰到問題時要能發現,但不需要成為談心的朋友。作者提出了多種「風格」的一對一會談,但我覺得重點應該還是「團隊目標」與「成員職涯」。作者認為身處在非管理職時候就應該在會談時與主管直接溝通自己的升職規畫以及需要達成的目標。
到了中階主管時,作者因為是工程師背景故特別強調持續寫code的重要性。作者認為至少要能了解公司整個coding到commit的流程,不然很有可能無法提早發現許多重要的問題。如果沒辦法自己commit code至少要做code review或嘗試pair programming。另外他也建議在工作之外參與side project、寫blog或是參加技術類社群活動。
重點寫在前面,若看過「鳳凰專案」就不需要再看「獨角獸專案」。但若你還沒看過鳳凰專案,建議你先看過鳳凰專案後還覺得意猶未盡再來看「獨角獸專案」。這兩本書其實是寫在同樣的時空背景,同一家公司的同一個時間但平行敘述兩個觀點。
鳳凰專案是從「Ops的中高管理階層」來看DevOps,而獨角獸專案則是從「Dev工程師與架構師」的角度來看DevOps,兩本書一樣是引人入勝的小說,相信投身於軟體相關工作的人讀完都會心有戚戚焉。不過鳳凰專案是用第一人稱的觀點,但獨角獸專案卻改用第三人稱的視角,而更多的登場角色讓這次故事造成一些混亂。中譯本的序建議讀者第一次先把這本書當作小說看,第二次閱讀再細細品嘗「核心業務與脈絡業務」及「系統組織的五大理念」其中的奧妙。我非常贊成,可以的話最好還可以兩本書同時交叉閱讀。
兩本書都以時序分成三部曲:
部署後的model隨時間改變,可能會漸漸不合用。像是垃報郵件過濾器、股票預測等等,環境可能會隨時間或是被model影響而改變。在部署後我們必需持續評估model的預測準確度,必要時觸發重新訓練的機制。
有些model部署的環境可能沒有可靠的網路環境,邊緣裝置是常見的情境。使用Two-Phase Predictions設計時,將問題拆成兩個部份。先從比較小的,比較便宜、可部置到邊緣裝置的model開始。例如先從「ok google」的偵測當作第一個model,接下來的複雜的語音辨識當作第二個問題,只有在使用者提出問題時運行。
因為網路封包傳輸並不總能保證順序性,另外我們的model也有可能部署在分散環境中。Keyed predictions會在每一個預測請求中加入一個unique key value,把這個unique key當作一個model的feature,但model不做任何運作直接把此key value當作output。這樣可以讓使用者從眾多預測請求中輕鬆分辦出正確的順序。
作者定義input必須先進行transform才能變成model的feature,例如文字檔案必須先進行embedding才能轉換成model使用的feature。也就是說作者把這些reprocessing或是feature engineering叫作「transform」。一但訓練資料完成transform,model在訓練時並不需要知道如何transform。但在部署model到環境之後,必需將input做transform才能運作。故這些transform的方法必須好好保存。
在分割training/validation/test data時,我們常用亂數分割的方法。但這個分割方法是有能力「重現」或是儲存。
我們使用的資料格式(schema)是有可能改變的。例如我們原本有兩種支付方式:現金或卡片,但我們後來可能會把「卡片」再細分三類:信用卡、簽帳金融卡或禮物卡。我們必須讓model能同時處理新資料與舊的資料。
有的問題是一連串時間相依的輸入,之前的輸入可能會影響之後輸入的預測結果。
例如要預測Dallas Fort Worth (DFW)機場抵達的班機是否為「abnormal arrival delays」,一個班機的延遲是否達到異常程度會受到最近兩小時其他班機的延遲狀態影響。我們收到一個班機抵達資訊可能只是一個timestamp:
2010-02-03 08:45:00,19.0
如上面八點四十五抵達的班機延遲19分鐘,但要從這點資訊來做預測幾乎不可能。這種情況我們可能要前幾個班機抵達的資料,例如:
2010-02-03 06:45:00,?
2010-02-03 06:?:00,?
...
2010-02-03 08:45:00,19.0
這種情境無法只對單一班機做inference,必需提供之前的資訊給model,即「Windowed inference」
若我們已經有一個已在使用的預測系統,新的model就應該以其為比較的標的。但若這個新model沒有已存在的標的可做比較,我們就需要一個「Heuristic Benchmark」
這本書在amazon剛出版沒多久很快就獲得不少好評,年初就買來但一直沒時間好好閱讀。剛好最近博客來需要湊單就連中文版一起下定。譯者是賴屹民,之前就買過這位作者的「流暢的Python」與「精通機器學習」「PHP、MySQL與JavaScript學習手冊」,只能說這次翻譯還是一樣無法恭維。
與四人幫的「設計模式」不同,這本書雖然有一些tf與BigQuery的範例碼,但基本都是在講ML的常用技巧並沒有太多程式架構,命名還不如改成歐萊禮的「CookBook」系列比較合適。
總的來說這本書還是很推薦,不少技巧都很實用且有價值,雖然有些pattern可能學過基本ML的人都會覺得基礎,但仔細閱讀會發現作者會提到其他關連知識或提示,閱讀時建議不要因為看標題就覺得學過而跳過。
當使用iterator寫一些刁鑽的程式碼時,itertools也許會有現成的工具。
import itertools
it = itertools.chain([1, 2, 3], [4, 5, 6])
print(list(it))
# [1, 2, 3, 4, 5, 6]
it = itertools.repeat('hello', 3)
print(list(it))
# ['hello', 'hello', 'hello']
it = itertools.cycle([1, 2])
result = [next(it) for _ in range(10)]
print(result)
# [1, 2, 1, 2, 1, 2, 1, 2, 1, 2]
it1, it2, it3 = itertools.tee(['first', 'second'], 3)
print(list(it1))
print(list(it2))
print(list(it3))
['first', 'second']
['first', 'second']
['first', 'second']
tee可以平行生出多個iterators,但要小心記憶體可能會使用很多。
keys = ['one', 'two', 'three']
values = [1, 2]
normal = list(zip(keys, values))
print('zip: ', normal)
it = itertools.zip_longest(keys, values, fillvalue='nope')
print('zip_longest:', list(it))
zip_longest類似內建的zip,只是zip會以最短的iterator回傳,而zip_longest會以最長的iterator回傳。預設會以None來補上缺值。
zip: [('one', 1), ('two', 2)]
zip_longest: [('one', 1), ('two', 2), ('three', 'nope')]
values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
first_five = itertools.islice(values, 5)
print('First five: ', list(first_five))
middle_odds = itertools.islice(values, 2, 8, 2)
print('Middle odds:', list(middle_odds))
First five: [1, 2, 3, 4, 5]
Middle odds: [3, 5, 7]
islice可以做出類似list的slice功能
values= [1, 2, 3, 4, 5, 4, 3, 2, 1]
it = itertools.takewhile(lambda x: x < 4, values)
print(list(it))
[1, 2, 3]
takewhile需要傳入一個callable object,當其回傳為False會停止iterator。另外有一個dropwhile會從其回傳True的時候開始iterator。
evens = lambda x: x % 2 == 0
filter_result = filter(evens, values)
print('Filter: ', list(filter_result))
filter_false_result = itertools.filterfalse( evens, values)
print('Filter false: ', list(filter_false_result))
Filter: [2, 4, 4, 2]
Filter false: [1, 3, 5, 3, 1]
filter_false就是內建的filter的相反。
python有簡單的語法來使用function decorators,但有可能造成一些不想要的副作用,例如可能會把help給覆蓋。
def trace(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
print(f'{func.__name__}({args!r}, {kwargs!r}) -> {result!r}')
return result
return wrapper
@trace
def fibonacci(n):
"""Return the n-th Fibonacci number"""
if n in (0, 1):
return n
return (fibonacci(n - 2) + fibonacci(n -1))
# @trace 等於是 fibonacci = trace(fibonacci)
fibonacci(4)
help(fibonacci)
import pickle
pickle.dumps(fibonacci)
fibonacci((0,), {}) -> 0 fibonacci((1,), {}) -> 1 fibonacci((2,), {}) -> 1 fibonacci((1,), {}) -> 1 fibonacci((0,), {}) -> 0 fibonacci((1,), {}) -> 1 fibonacci((2,), {}) -> 1 fibonacci((3,), {}) -> 2 fibonacci((4,), {}) -> 3 Help on function wrapper in module __main__: wrapper(*args, **kwargs) Traceback (most recent call last): File "C:/Users/hans/Desktop/item 26.py", line 22, inpickle.dumps(fibonacci) AttributeError: Can't pickle local object 'trace. .wrapper'
在wrapper function加上wraps可以修好help跟pickle
from functools import wraps
def trace(func):
@wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
print(f'{func.__name__}({args!r}, {kwargs!r}) -> {result!r}')
return result
return wrapper
help(fibonacci)
print(pickle.dumps(fibonacci))
Help on function fibonacci in module __main__: fibonacci(n) Return the n-th Fibonacci number b'\x80\x04\x95\x1a\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\tfibonacci\x94\x93\x94.'
def move(period, speed):
for _ in range(period):
yield speed
def pause(delay):
for _ in range(delay):
yield 0
def animate():
for delta in move(4, 5.0):
yield delta
for delta in pause(3):
yield delta
for delta in move(2, 3.0):
yield delta
def render(delta):
print(f'Delta: {delta:.1f}')
# Move the imgaes onscreen
...
def run(func):
for delta in func():
render(delta)
run(animate)
想像我們在螢幕上用不同速度輪播不同照片來做成動畫,有的時候也會在中間暫停一下。
Delta: 5.0
Delta: 5.0
Delta: 5.0
Delta: 5.0
Delta: 0.0
Delta: 0.0
Delta: 0.0
Delta: 3.0
Delta: 3.0
改用yield from不僅是看起來更簡潔,重點是可以獲得更佳的運算效能。
def animate_composed():
yield from move(4, 5.0)
yield from pause(3)
yield from move(2, 3.0)
run(animate_composed)
使用print()時通常是輸出所謂human readable字串,但有時會反而會造成困擾
print(5)
#5
print('5')
#5
int_value = 5
str_value = '5'
print(f'{int_value} == {str_value} ?')
#5 == 5 ?
我們可能分不清這個變數是字串’5’還是數值「5」。
另外有些字元可能不是printable,故print會看不到東西。
這時候可以改用rerp(),會改成所謂的printable representation的輸出。
a = '\x07'
print(a)
#
print(repr(a))
#'\x07'
Python有一個eval()可以把representation轉回成變數,但要知道eval()很容易造成問題,不該輕易使用。
b = eval(repr(a))
assert a == b
print(repr(5))
#5
print(repr('5'))
#'5'
print(f'{int_value}r != {str_value}r')
#5r != 5r
一些自訂的object預設的print()輸出可能會不如預期
class OpaqueClass:
def __init__(self, x, y):
self.x = x
self.y = y
obj = OpaqueClass(1, 'foo')
print(obj)
<main.OpaqueClass object at 0x037C9088>
我們可以用__repr__來設計repr()的預設行為,另外format string也可以用{}r來輸出
printable representation
class BetterClass:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f'BetterClass({self.x!r}, {self.y!r})'
obj = BetterClass(2, 'bar')
print(obj)
BetterClass(2, ‘bar’)
class BetterClass2:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f'BetterClass2({self.x!r}, {self.y!r})'
def __str__(self):
return f'in __str__ {self.x}, {self.y}'
obj = BetterClass2(3, 'bar')
print(obj)
print(repr(obj)
in __str__ 3, bar
BetterClass2(3, ‘bar’)
另外__str__可以設計print()的行為。
若__str__沒有修改,但有__repr__修改,print()會呼叫它。
obj = OpaqueClass(4, 'baz')
print(obj.__dict__)
{‘x’: 4, ‘y’: ‘baz’}
若不能修改物件,我們可以改用__dict__來得到物件的成員所組成的dict
stock = {
'nails': 125,
'screws': 35,
'wingnuts': 8,
'washers': 24,
}
def get_batches(count, size):
return count // size
comprehensions是python常用的一個功能,可以快速生成list, dict, set等變數。但有時會出現一些重覆的計算…
order = ['screws', 'wingnuts', ' clips']
found = {name: get_batches(stock.get(name, 0), 8)
for name in order
if get_batches(stock.get(name, 0), 8)}
print(found)
{‘screws’: 4, ‘wingnuts’: 1}
現在我們可以用assignment expressions( :=,又稱walrus operator)來減少這些重覆語句的出現。
found = {name: batches for name in order
if (batches := get_batches(stock.get(name, 0), 8))}
print(found)
不過要小心assignment expressions可能會汙染comprehensions外的scope,所以本書只建議在if的區域使用assignment expressions。
half = [(last := count // 2) for count in stock.values()]
print(last)
#12
print(batches)
#0
但實際上if區域的變數也是會汙染,書中的建議原因不太確定為何。