T 沙龍 2018 年 1 月上海第 10 次線下活動總結

技術編輯:冬瓜 & EyreFreehtml

運營編輯:千葉知風前端

零. 前言

做爲 2018 年的開箱活動,咱們爲你們帶來了 AI 與算法專場。過去的一年裏 AI 有多火熱,想必你們都很是清楚,因此雖然是 Swift 沙龍,但咱們仍然特地爲你們準備了這麼一場以 AI 爲主題的分享活動。python

下面是咱們沙龍技術編輯童鞋對這三個 Topic 的一些總結:git

一. 自適應學習 - 機器學習在開心詞場中的應用

分享嘉賓 - 滬江網數據挖掘總監王新義程序員

王老師現任滬江網數據挖掘總監,主要負責數據挖掘和機器學習技術在互聯網教育中的應用,包括用戶畫像、推薦系統、知識圖譜、自適應學習系統等。加入滬江前積累了 6 年以大數據和數據挖掘爲核心的行業數據應用經驗、流程及知識。熟悉 Hadoop 架構,對數據挖掘、機器學習/深度學習和大數據方向應用有較豐富的應用經驗和技術積累。github

王老師帶來的一套「自適應學習」的方案。首先向咱們展現了機器學習在滬江的業務各個層級中的運用狀況,並用如下結構圖進行總概。golang

自適應詞彙量測試

爲了照顧在場多數的前端(iOS)工程師,王老師具體介紹了一個場景,即 自適應詞彙量測試。首先先分析瞭如今市面上的背單詞軟件的詞彙量分析功能,其原理通常分爲兩種:算法

  • 靜態試卷:特色:1. 每一個學生所作的題目相同;2. 在會作的容易題和不會作的難題上浪費較多時間,影響用戶體驗。
  • 動態交互式測試:特色:1. 每一個學生所作的題目不相同;2. 下一道題目根據歷史作題反饋動態改變;3. 算法能夠聚焦於算法不肯定的題目請學生回答,而避免在確定會作和確定不會作的題目浪費太多時間。

顯然,其 動態交互式測試 法師優於原始的靜態試卷法,因而咱們引入一種評估流程:算法 → 單詞 → 用戶做答。這裏須要套用一種算法,根據用戶的作題記錄評估其能力後反饋出一個新的單詞讓用戶來做答,而後根據做答狀況,繼續經過算法來評估能力,並持續這個循環。再必定量的循環後,咱們並能夠預估用戶的詞彙量。這個詞彙量的關鍵是用戶的 能力狀況,而這個值也是與單詞難度可比。數據庫

Item Response Theory - IRT 教學模型

爲了評估用戶的能力狀況並使其量化,咱們引入了 IRT 模型,來解決問題。IRT 是用來分析考試成績或問卷調查數據的數學模型,這些模型的目標是來肯定其潛在特徵(Latent Trait)是否能夠經過測試題反映出來,以及測試題和被測者之間的互動關係,下面給出其數學模型。wikipedia編程

\begin{aligned}
P(\theta)=c+(1-c)\int_{-\infty}^{a(\theta-b)}\frac{e^{-t^2}}{2}\sqrt{2\pi}dt
\end{aligned}

這個 3 參數Normal-ogive 模型稱爲 3PN。爲了計算方便,能夠用 3PL 模型來替代,運用在數值處理:

\begin{aligned}
P(\theta)=c+\frac{1-c}{1+e^{-Da(\theta-b)}}
\end{aligned}

其中,D 爲常數 1.7。a 叫作區分度參數(Item Discrimination),a / 4 的值是該曲線拐點處的斜率,即斜率最大值。b 叫作難度參數(Item Difficulty),也是在圖像上最陡處所對應的 \theta 值。當 b 增長時,曲線會向右移動,即便此時 \theta 保持不變,可是答題的正確率降低,說明題目的難度增長,反之下降。c 通常稱做猜想參數(Guessing Parameter),當用戶的能力值很低時,但他作對題目的機率 c ,即爲猜想的能力。

當前場景下,咱們套用 IRT 模型便可轉化表達:

\begin{aligned}
P(A|b, c)=\frac{1}{1+e^{-1.4 * (c - b)}}
\end{aligned}

其中 A 爲答對題目事件,B 和 C 分別表示單詞難度和人的能力量化值。爲了評估人的能力,咱們引入極大似然估計

知識追蹤 DKT

自適應學習是如今教育科技領域談得比較多的一個概念,它的核心問題能夠用一句話歸納,即 經過個性化的學習路徑規劃,提升學生的學習效率。爲了對學習序列建模,並評估學生各個時刻的能力,滬江引入了 Deep Knowledge Tracing(DKT)模型,這個模型是由 Stanford 大學的 Piech Chris 等人在 NIPS 2015發表的,其本質是一個 Seq2Seq 的 RNN 模型。

如下是該模型的結構圖:

x 表明問題是否做對的編碼,y 表明對於全部問題作對的機率,h 表明學生隱含的知識水平。再來看該模型的層次結構:

假設題庫中總共有 4 道題,則輸出層的數量爲 4,對應各題回答正確的機率。另外咱們已知這些題目的結果數目,因此輸入層的節點數就能夠用 x_{n}=y_{n}\times a_{n} 來求得。進而,咱們將輸入層所有連接到 RNN 的隱層,接着創建隱層到輸出層的全鏈接,最後使用 Sigmoid 函數做爲激活函數,就完成了基礎的 DKT 模型。在 PPT 中展現了不少訓練集的訓練結果,這裏是 鏈接

簡單瞭解機器學習思想

王老師在講述完機器學習在滬江業務中的運用後,又簡明地介紹了什麼是機器學習的思想。他在現場出了這麼一個題目

求出知足這個式子的全部可能性:xxx * x - xxxx = 0。而且其中每一個 x 表明一位數,切知足這八位數分別是 1 ~ 8。例若有這麼一組知足的算式 453 * 6 - 2718 = 0

用傳統的計算機算法思惟來解決這個問題,咱們直接使用 全排列 對每一種狀況進行枚舉便可實現,全排列算法複雜度爲 O(n!)

# 全排列解法 1:使用多層嵌套遍歷實現
for i in range(1,9):
    for j in range(1,9):
        for k in range(1,9):
            for m in range(1,9):
                ss=str(i)+str(j)+str(k)+str(m)+str((i*100+j*10+k)*m)
                if len(ss)==8 and len(set(list(ss)))==8 and  '0' not in ss and '9' not in ss :
                    print(ss[0:3]+'*'+ss[3]+'='+ss[4:])
                    
# 全排列解法 2:itertools 包中已經有 permutations 全排列方法
from itertools import permutations
print([''.join(x[:3])+'*'+''.join(x[3])+'='+''.join(x[4:]) for x in list(permutations('12345678', 8)) if int(''.join(x[:3]))*int(x[3])==int(''.join(x[4:]))])

for x in list(permutations('12345678',8)):
    if int(''.join(x[:3]))*int(x[3])==int(''.join(x[4:])):
        print(x)
        print(''.join(x[:3])+'*'+''.join(x[3])+'-'+''.join(x[4:])+'='+str(int(''.join(x[:3]))*int(x[3])-int(''.join(x[4:]))))
複製代碼

若是咱們從機器學習的角度來考慮整個問題,首先須要定義一個損失函數,用來評估模型的預測值和真實值的不一致程度,它是一個非負實值函數,若是損失函數越小,則模型的魯棒性越好。這裏咱們的損失函數直接用 xxx * x - xxxx 的絕對值便可描述。

第二,梯度降低函數。因爲沒有一個可靠的降低方式,因此這裏咱們採用 SGD 隨機梯度降低方式,在取值範圍內作隨機取值來實現梯度降低。

x=[1,2,3,4,5,6,7,8]
ss=train(x)
ss
x

#%%
import random
def lose(x):   # 損失函數
    return abs((int(x[0])*100+int(x[1])*10+int(x[2]))*int(x[3])-(int(x[4])*1000+int(x[5])*100+int(x[6])*10+int(x[7])))

def swap(x,i,j):  #
    xx=x.copy()
    tmp=xx[i]
    xx[i]=xx[j]
    xx[j]=tmp
    return xx

def sgd(x):  # 梯度降低
    data_dict={}
    for i in range(0,7):
        for j in range(i+1,8):
            x_tmp=swap(x,i,j)
            lose_v_tmp=lose(x_tmp)
            data_dict[''.join(x_tmp)]=lose_v_tmp
    dd=sorted(data_dict.items(),key=lambda item:item[1])
    #print(dd[1][0])
    return list(dd[random.randint(0, 10)][0])
 
def train(x):
    while lose(x)>0 :
        x=sgd(x)
        print(x,'lose value:',lose(x))
    return x

x=['1','2','3','4','5','6','7','8']
ss=train(x)
print()
print(ss)
複製代碼

在隨機梯度中輸出的損失值,會發現會有抖動,不會按照正確的方向持續降低,這個波動特色會使得優化的方向從當前具有極小值點跳躍到另外一個更好的局部極小值點,對於非凸函數,最終收斂域一個較好的局部極值點,甚至是全局極值點。

在 SGD 中,因爲波動,所以迭代次數不少,收斂速度慢。不過最終會和全量梯度降低算法同樣,具備相同的收斂性,即凸函數收斂於全局極值點,非凸損失函數收斂於局部極值點。

爲了優化其收斂性,王老師還提供了一種 差分進化算法(Differential Evolution) 方案,詳細代碼轉至 Github 進行學習,這裏不作贅述。


二. 初探地圖類 App 後端那些事:空間索引算法

分享嘉賓 - 冰霜

冰霜,瞭解 iOS 開發,略懂 JavaScript, Go, C, C++。GitHub:@halfrost,微博:@halfrost

第二位是 iOS 圈內你們耳熟能詳的冰霜,本名於德志,目前在餓了麼從事 Go 後端開發。以前他一直致力於鑽研 iOS 端開發技巧,編寫了大量技術文章,涉獵 Go、JavaScript、iOS 開發等多個技術領域,名副其實的全棧工程師。這是他的第一次公開技術分享,他結合自身的工做實踐,爲咱們全面細緻地介紹了空間索引算法在地圖類 App 中的使用。

冰霜分四段給你們進行了講解:

1. 高效的多維空間點索引算法 — Geohash

1.1 Geohash 算法簡介

Geohash 是一種地理編碼,由 Gustavo Niemeyer 發明的。它是一種分級的數據結構,把空間劃分爲網格。Geohash 屬於空間填充曲線中的 Z 階曲線(Z-order curve)的實際應用。

何爲 Z 階曲線?

上圖就是 Z 階曲線。這個曲線比較簡單,生成它也比較容易,只須要把每一個 Z 首尾相連便可。

Z 階曲線一樣能夠擴展到三維空間。只要 Z 形狀足夠小而且足夠密,也能填滿整個三維空間。

Geohash 有一個和 Z 階曲線相關的性質,那就是一個點附近的地方(但不絕對) hash 字符串老是有公共前綴,而且公共前綴的長度越長,這兩個點距離越近。

因爲這個特性,Geohash 就經常被用來做爲惟一標識符。用在數據庫裏面可用 Geohash 來表示一個點。Geohash 這個公共前綴的特性就能夠用來快速的進行鄰近點的搜索。越接近的點一般和目標點的 Geohash 字符串公共前綴越長(可是這不必定,也有特殊狀況,下面舉例會說明)

Geohash 也有幾種編碼形式,常見的有 2 種,base 32 和 base 36。

Decimal 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Base 32 0 1 2 3 4 5 6 7 8 9 b c d e f g
Decimal 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
Base 32 h j k m n p q r s t u v w x y z

base 36 的版本對大小寫敏感,用了 36 個字符,「23456789bBCdDFgGhHjJKlLMnNPqQrRtTVWX」。

Decimal 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Base 36 2 3 4 5 6 7 8 9 b B C d D F g G h H j
Decimal 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
Base 36 J K I L M n N P q Q r R t T V W X

1.2 Geohash 具體實現

接下來用 Go 實現一下 Geohash 算法:

package geohash

import (
	"bytes"
)

const (
	BASE32                = "0123456789bcdefghjkmnpqrstuvwxyz"
	MAX_LATITUDE  float64 = 90
	MIN_LATITUDE  float64 = -90
	MAX_LONGITUDE float64 = 180
	MIN_LONGITUDE float64 = -180
)

var (
	bits   = []int{16, 8, 4, 2, 1}
	base32 = []byte(BASE32)
)

type Box struct {
	MinLat, MaxLat float64 // 緯度
	MinLng, MaxLng float64 // 經度
}

func (this *Box) Width() float64 {
	return this.MaxLng - this.MinLng
}

func (this *Box) Height() float64 {
	return this.MaxLat - this.MinLat
}

// 輸入值:緯度,經度,精度(geohash的長度)
// 返回geohash, 以及該點所在的區域
func Encode(latitude, longitude float64, precision int) (string, *Box) {
	var geohash bytes.Buffer
	var minLat, maxLat float64 = MIN_LATITUDE, MAX_LATITUDE
	var minLng, maxLng float64 = MIN_LONGITUDE, MAX_LONGITUDE
	var mid float64 = 0

	bit, ch, length, isEven := 0, 0, 0, true
	for length < precision {
		if isEven {
			if mid = (minLng + maxLng) / 2; mid < longitude {
				ch |= bits[bit]
				minLng = mid
			} else {
				maxLng = mid
			}
		} else {
			if mid = (minLat + maxLat) / 2; mid < latitude {
				ch |= bits[bit]
				minLat = mid
			} else {
				maxLat = mid
			}
		}

		isEven = !isEven
		if bit < 4 {
			bit++
		} else {
			geohash.WriteByte(base32[ch])
			length, bit, ch = length+1, 0, 0
		}
	}

	b := &Box{
		MinLat: minLat,
		MaxLat: maxLat,
		MinLng: minLng,
		MaxLng: maxLng,
	}

	return geohash.String(), b
}

複製代碼

1.3 Geohash 的優缺點

Geohash 的優勢很明顯,它利用 Z 階曲線進行編碼。而 Z 階曲線能夠將二維或者多維空間裏的全部點都轉換成一維曲線。在數學上成爲分形維。而且 Z 階曲線還具備局部保序性。

Z 階曲線經過交織點的座標值的二進制表示來簡單地計算多維度中的點的z值。一旦將數據被加到該排序中,任何一維數據結構,例如二叉搜索樹,B樹,跳躍表或(具備低有效位被截斷)哈希表 均可以用來處理數據。經過 Z 階曲線所獲得的順序能夠等同地被描述爲從四叉樹的深度優先遍歷獲得的順序。

這也是 Geohash 的另一個優勢,搜索查找鄰近點比較快。

Geohash 的缺點之一也來自 Z 階曲線。

Z 階曲線有一個比較嚴重的問題,雖然有局部保序性,可是它也有突變性。在每一個 Z 字母的拐角,都有可能出現順序的突變。

看上圖中標註出來的藍色的點點。每兩個點雖然是相鄰的,可是距離相隔很遠。看右下角的圖,兩個數值鄰近紅色的點二者距離幾乎達到了整個正方形的邊長。兩個數值鄰近綠色的點也達到了正方形的一半的長度。

Geohash 的另一個缺點是,若是選擇很差合適的網格大小,判斷鄰近點可能會比較麻煩。

看上圖,若是選擇 Geohash 字符串爲6的話,就是藍色的大格子。紅星是美羅城,紫色的圓點是搜索出來的目標點。若是用 Geohash 算法查詢的話,距離比較近的多是 wtw37p,wtw37r,wtw37w,wtw37m。可是其實距離最近的點就在 wtw37q。若是選擇這麼大的網格,就須要再查找周圍的8個格子。

若是選擇 Geohash 字符串爲7的話,那變成黃色的小格子。這樣距離紅星星最近的點就只有一個了。就是 wtw37qw。

若是網格大小,精度選擇的很差,那麼查詢最近點還須要再次查詢周圍 8 個點。

2. 空間填充曲線

在數學分析中,有這樣一個難題:可否用一條無限長的線,穿過任意維度空間裏面的全部點?通過人們的不斷探索,除了上面的 Z 階曲線外,還發現下列曲線都能實現這一需求:

2.1 Peano 曲線

2.2 龍曲線

2.3 高斯帕曲線

2.4 Koch 曲線

2.5 摩爾定律曲線

2.6 謝爾賓斯基曲線 & 奧斯古德曲線

2.7 希爾伯特曲線

3. 多維空間點索引算法之二 — Google S²

在介紹這個重量級算法以前,先解釋一些這個算法的名字由來。S2實際上是來自幾何數學中的一個數學符號 S²,它表示的是單位球。S2 這個庫實際上是被設計用來解決球面上各類幾何問題的。值得提的一點是,除去 golang 官方 repo 裏面的 geo/s2 完成度目前只有40%,其餘語言,Java,C++,Python 的 S2 實現都完成100%了。本此分享的講解以 Go 的這個版本爲主。

接下來就看看怎麼用 S2 來解決多維空間點索引的問題的。

3.1 球面座標轉換

按照以前咱們處理多維空間的思路,先考慮如何降維,再考慮如何分形。

衆所周知,地球是近似一個球體。球體是一個三維的,如何把三維降成一維呢?大體原理以下圖所示:

再進一步,咱們能夠和球面上的經緯度聯繫起來,因而地球上任意的一個經緯度的點,就能夠轉換成 f(x,y,z):

3.2 球面變平面

接下來一步 S2 把球面碾成平面。怎麼作的呢?首先在地球外面套了一個外切的正方體,以下圖:

從球心向外切正方體6個面分別投影。S2 是把球面上全部的點都投影到外切正方體的6個面上。這裏簡單的畫了一個投影圖,上圖左邊的是投影到正方體一個面的示意圖,實際上影響到的球面是右邊那張圖:

投影到正方體之後,咱們就能夠把這個正方體展開了:

3.3 球面矩形投影修正

上一步咱們把球面上的球面矩形投影到正方形的某個面上,造成的形狀相似於矩形,可是因爲球面上角度的不一樣,最終會致使即便是投影到同一個面上,每一個矩形的面積也不大相同:

上圖就表示出了球面上個一個球面矩形投影到正方形一個面上的狀況。

通過實際計算髮現,最大的面積和最小的面積相差5.2倍。見上圖左邊。相同的弧度區間,在不一樣的緯度上投影到正方形上的面積不一樣。

如今就須要修正各個投影出來形狀的面積。如何選取合適的映射修正函數就成了關鍵。目標是能達到上圖右邊的樣子,讓各個矩形的面積儘可能相同。

這塊轉換的代碼在 C++ 的版本里面纔有詳細的解釋,在 Go 的版本里面只一筆帶過了。害筆者懵逼了很久。

面積比率 邊比率 對角線比率 ToPointRaw ToPoint FromPoint
線性變換 5.200 2.117 2.959 0.020 0.087 0.085
tan()變換 1.414 1.414 1.704 0.237 0.299 0.258
二次變換 2.082 1.802 1.932 0.033 0.096 0.108

線性變換是最快的變換,可是變換比最小。tan() 變換可使每一個投影之後的矩形的面積更加一致,最大和最小的矩形比例僅僅只差0.414。能夠說很是接近了。可是 tan() 函數的調用時間很是長。若是把全部點都按照這種方式計算的話,性能將會下降3倍。

最後谷歌選擇的是二次變換,這是一個近似切線的投影曲線。它的計算速度遠遠快於 tan() ,大概是 tan() 計算的3倍速度。生成的投影之後的矩形大小也相似。不過最大的矩形和最小的矩形相比依舊有2.082的比率。

通過修正變換之後,u,v都變換成了s,t。值域也發生了變化。u,v的值域是[-1,1],變換之後,是s,t的值域是[0,1]。

至此,小結一下,球面上的點S(lat,lng) -> f(x,y,z) -> g(face,u,v) -> h(face,s,t)。目前總共轉換了4步,球面經緯度座標轉換成球面xyz座標,再轉換成外切正方體投影面上的座標,最後變換成修正後的座標。

3.4 點與座標軸點相互轉換

在 S2 算法中,默認劃分 Cell 的等級是30,也就是說把一個正方形劃分爲 2^30 * 2^30個小的正方形。

那麼上一步的s,t映射到這個正方形上面來,對應該如何轉換呢?

s,t的值域是[0,1],如今值域要擴大到[0,2^30^-1]。

3.5 座標軸點與希爾伯特曲線 Cell ID 相互轉換

最後一步,如何把 i,j 和希爾伯特曲線上的點關聯起來呢?在變換以前,先來解釋一下定義的一些變量。

posToIJ 表明的是一個矩陣,裏面記錄了一些單元希爾伯特曲線的位置信息。

把 posToIJ 數組裏面的信息用圖表示出來,以下圖:

同理,把 ijToPos 數組裏面的信息用圖表示出來,以下圖:

posToOrientation 數組裏面裝了4個數字,分別是1,0,0,3。 lookupIJ 和 lookupPos 分別是兩個容量爲1024的數組。這裏面分別對應的就是希爾伯特曲線 ID 轉換成座標軸 IJ 的轉換表,和座標軸 IJ 轉換成希爾伯特曲線 ID 的轉換表。

這裏的話因爲設計比較複雜的數學計算,就不進一步展開了,有興趣的同窗能夠參考冰霜大佬的 高效的多維空間點索引算法 — Geohash 和 Google S2 一文。

3.6 S2 Cell ID 數據結構

最後須要來談談 S2 Cell ID 數據結構,這個數據結構直接關係到不一樣 Level 對應精度的問題。

由上一章咱們知道,因爲投影的緣由,因此致使投影以後的面積依舊有大小差異。這裏推算的公式比較複雜,就不證實了,具體的能夠看文檔。這就是最大最小面積和平均面積的倍數關係:

level min area max area average area units Random cell 1 (UK) min edge length Random cell 1 (UK) max edge length Random cell 2 (US) min edge length Random cell 2 (US) max edge length Number of cells
00 85011012.19 85011012.19 85011012.19 km2 7842 km 7842 km 7842 km 7842 km 6
01 21252753.05 21252753.05 21252753.05 km2 3921 km 5004 km 3921 km 5004 km 24
02 4919708.23 6026521.16 5313188.26 km2 1825 km 2489 km 1825 km 2489 km 96
03 1055377.48 1646455.50 1328297.07 km2 840 km 1167 km 1130 km 1310 km 384
04 231564.06 413918.15 332074.27 km2 432 km 609 km 579 km 636 km 1536
05 53798.67 104297.91 83018.57 km2 210 km 298 km 287 km 315 km 6K
06 12948.81 26113.30 20754.64 km2 108 km 151 km 143 km 156 km 24K
07 3175.44 6529.09 5188.66 km2 54 km 76 km 72 km 78 km 98K
08 786.20 1632.45 1297.17 km2 27 km 38 km 36 km 39 km 393K
09 195.59 408.12 324.29 km2 14 km 19 km 18 km 20 km 1573K
10 48.78 102.03 81.07 km2 7 km 9 km 9 km 10 km 6M
11 12.18 25.51 20.27 km2 3 km 5 km 4 km 5 km 25M
12 3.04 6.38 5.07 km2 1699 m 2 km 2 km 2 km 100M
13 0.76 1.59 1.27 km2 850 m 1185 m 1123 m 1225 m 402M
14 0.19 0.40 0.32 km2 425 m 593 m 562 m 613 m 1610M
15 47520.30 99638.93 79172.67 m2 212 m 296 m 281 m 306 m 6B
16 11880.08 24909.73 19793.17 m2 106 m 148 m 140 m 153 m 25B
17 2970.02 6227.43 4948.29 m2 53 m 74 m 70 m 77 m 103B
18 742.50 1556.86 1237.07 m2 27 m 37 m 35 m 38 m 412B
19 185.63 389.21 309.27 m2 13 m 19 m 18 m 19 m 1649B
20 46.41 97.30 77.32 m2 7 m 9 m 9 m 10 m 7T
21 11.60 24.33 19.33 m2 3 m 5 m 4 m 5 m 26T
22 2.90 6.08 4.83 m2 166 cm 2 m 2 m 2 m 105T
23 0.73 1.52 1.21 m2 83 cm 116 cm 110 cm 120 cm 422T
24 0.18 0.38 0.30 m2 41 cm 58 cm 55 cm 60 cm 1689T
25 453.19 950.23 755.05 cm2 21 cm 29 cm 27 cm 30 cm 7e15
26 113.30 237.56 188.76 cm2 10 cm 14 cm 14 cm 15 cm 27e15
27 28.32 59.39 47.19 cm2 5 cm 7 cm 7 cm 7 cm 108e15
28 7.08 14.85 11.80 cm2 2 cm 4 cm 3 cm 4 cm 432e15
29 1.77 3.71 2.95 cm2 12 mm 18 mm 17 mm 18 mm 1729e15
30 0.44 0.93 0.74 cm2 6 mm 9 mm 8 mm 9 mm 7e18

3.7 S2 與 Geohash 對比

Geohash 有12級,從5000km 到 3.7cm。中間每一級的變化比較大。有時候可能選擇上一級會大不少,選擇下一級又會小一些。好比選擇字符串長度爲4,它對應的 cell 寬度是39.1km,需求多是50km,那麼選擇字符串長度爲5,對應的 cell 寬度就變成了156km,瞬間又大了3倍了。這種狀況選擇多長的 Geohash 字符串就比較難選。選擇很差,每次判斷可能就還須要取出周圍的8個格子再次進行判斷。Geohash 須要 12 bytes 存儲。

S2 有30級,從 0.7cm² 到 85,000,000km² 。中間每一級的變化都比較平緩,接近於4次方的曲線。因此選擇精度不會出現 Geohash 選擇困難的問題。S2 的存儲只須要一個 uint64 便可存下。

S2 庫裏面不只僅有地理編碼,還有其餘不少幾何計算相關的庫。地理編碼只是其中的一小部分。本文沒有介紹到的 S2 的實現還有不少不少,各類向量計算,面積計算,多邊形覆蓋,距離問題,球面球體上的問題,它都有實現。

S2 還能解決多邊形覆蓋的問題。好比給定一個城市,求一個多邊形剛恰好覆蓋住這個城市。

用相同的 Cell 也能夠達到相同的目的,上圖就是用相同 Level 的 Cell 覆蓋了整個聖保羅城市。

這些都是 Geohash 作不到的。多邊形覆蓋利用的是近似的算法,雖然不是嚴格意義上的最優解,可是實踐中效果特別好。

3.8 S2 的應用

S2 主要能用在如下 8 個地方:

  • 涉及到角度,間隔,緯度經度點,單位矢量等的表示,以及對這些類型的各類操做。
  • 單位球體上的幾何形狀,如球冠(「圓盤」),緯度 - 經度矩形,折線和多邊形。
  • 支持點,折線和多邊形的任意集合的強大的構造操做(例如聯合)和布爾謂詞(例如,包含)。
  • 對點,折線和多邊形的集合進行快速的內存索引。
  • 針對測量距離和查找附近物體的算法。
  • 用於捕捉和簡化幾何的穩健算法(該算法具備精度和拓撲保證)。
  • 用於測試幾何對象之間關係的有效且精確的數學謂詞的集合。
  • 支持空間索引,包括將區域近似爲離散「S2單元」的集合。此功能能夠輕鬆構建大型分佈式空間索引。
  • 最後一點空間索引相信在工業生產中使用的很是普遍。

4. 實際應用

最後,關於索引算法的應用場景,給出了一系列展現圖片:

另關於本次分享的內容,可參考冰霜 GitHub 的空間搜索系列博文:github.com/halfrost/Ha…


三. 機器學習介紹

分享嘉賓 - 梅元剛

第三位分享嘉賓是來自金山雲的梅元剛。中科院碩士,擅長圖像處理和機器學習,三星 Gear 360 全景視頻做者,金山雲美顏做者,金山雲 AI 畫質加強核心做者。因爲以前種種緣由接觸了 Swift 並接觸了 iOS 開發,本場沙龍,他以移動開發者的角度,帶着咱們簡單瞭解了機器學習。

梅老師主要分享了一些他在機器學習過程當中的積累,對入門很是有幫助。

機器學習的基本分類

監督學習就是標明一些數據是對的,另外一些數據是錯的,而後讓程序預測,新的數據是對的仍是錯的。因此說,有監督學習,必須是有標籤的。

無監督學習,顧名思義,就是不對數據進行標明,讓機器自動去判斷,哪些數據比較像,歸到一類等等。

強化學習或者叫作增強學習,是指什麼呢?在前邊的監督學習中,機器每次作出預測,都會知道結果對不對,可是在這裏卻不行,每次作出預測不會獲得對或者不對的結果,只會收到看似沒有半毛錢關係的反饋。因此強化學習不是依賴數據的標籤進行學習,而是依賴本身積累的反饋。強化學習適合學習交互過程,好比下圍棋(AlphaGo的成功就是強化學習的力量)。

卷積神經網絡

以下圖所示,展現了一個33的卷積核在55的圖像上作卷積的過程。每一個卷積都是一種特徵提取方式,就像一個篩子,將圖像中符合條件(激活值越大越符合條件)的部分篩選出來。不一樣的卷積核可以提取到圖像中的不一樣特徵,這裏有 在線 demo

v2-7fce29335f9b43bce1b373daa40cccba_hd

深度學習圖像應用

我在此不是要說明做者是怎麼作到這些的,而是讓你們看一些使人難以置信的結果。

藝術類

TYLE2PAINTS:強大的爲線稿上色的 AI

推薦理由:新一代的強大線稿上色 AI,可根據用戶上傳的自定義色彩給線稿進行上色。項目提供了在線使用網站,十分方便使用。

CycleGAN:生成對抗網絡圖像處理工具

這個工具功能十分強大,不只可將繪畫做品「還原」成照片(可理解爲是一個 「反濾鏡」),還能將夏天轉換成冬天,或將普通的馬轉化成斑馬。

184953_TSZk_2720166

make ASCII Art by Deep Learning

識別類

maskrcnn

使用遷移學習作動物臉部識別

牛其實不肯意看到人類的,他們會視人類爲捕食者,所以養牛場的工做人員會給牛羣帶來緊張情緒。那麼咱們就把農場的管理交給人工智能吧。

人工智能經過農場的攝像裝置得到牛臉以及身體情況的照片,進而經過深度學習對牛的情緒和健康情況進行分析,而後幫助農場主判斷出那些牛生病了,生了什麼病,那些牛沒有吃飽,甚至那些牛到了發情期。除了攝像裝置對牛進行「牛臉」識別,還能夠配合上可穿戴的智能設備,這會讓農場主更好的管理農場。這些數據上傳到雲服務器上,用本身開發的算法經過機器學習讓這些海量的原始數據變成直觀的圖表和信息發送到客戶那裏。這些信息包括奶牛的健康分析、發情期探測和預測、餵養情況、位置服務等。

圖像加強類

DeblurGAN

圖像超分辨率

RAISR 這項技術能利用機器學習,把低分辨率圖片轉爲高分辨率圖片。它的效果能達到甚至超過如今的超分辨率解決方案,同時速度提高大約 10 至 100 倍,且可以在普通的移動設備上運行。

Face2Face:扮演特朗普

斯坦福大學的一個小組作了一款名爲 Face2Face 的應用,這套系統可以利用人臉捕捉,讓你在視頻裏實時扮演另外一我的,簡單來說,就是能夠把你的面部表情實時移植到視頻里正在發表演講的美國總統身上。

加強學習

MIT最新課程 ——九小時速成深度學習&自動駕駛汽車

a minitaur duck

68747470733a2f2f63646e2e7261776769742e636f6d2f686172646d6172752f707962756c6c65745f616e696d6174696f6e732f38613663636166352f616e696d2f6d696e69746175722f6475636b5f6e6f726d616c5f736d616c6c2e676966

OpenAI 所訓練的一款人工智能算法在著名的電子競技遊戲 Dota2 國際邀請賽 The International 中,參與了 1V1 比賽環節,並壓倒性的擊敗了頂級電子競技選手 Dendi。

反覆攻破和修補本身的防火牆

Google 大腦的研究團隊建立了兩個深度學習網絡用於安全工具開發,他們讓其中一個不斷創造本身的加密算法,而後讓另外一個網絡去盡力攻破它。在兩套系統的反覆纏鬥後,第一個系統已經能生成很是優秀的安全加密算法。

AI能夠本身編程

DeepCoder 使用了一種叫作程序合成(Program Synthesis)的技術,其運行原理與程序員所作的事情差很少,就是從存在的軟件中獲取已知的代碼段,並將它們拼接到一塊兒執行新的程序。只要賦予 DeepCoder 中每一個片斷對應的輸入和輸出,程序就能夠「學習」到哪些代碼是咱們所須要的。

iOS實現

在 WWDC 2017 上,蘋果首次公佈了機器學習方面的動做。iOS 系統早已支持 Machine Learning 和 Computer Vision,但此次蘋果提供了更合理,容易上手的API,讓那些對基礎理論知識一竅不通的門外漢也能玩轉高大上的前沿科技。

此後,蘋果公司宣佈了能夠在設備上應用機器學習的兩種新技術:Core ML 和 MPS graph API(Core ML 構建於 MPS 之上,MPS 更底層)。

注意:運行 demo 須要使用 Xcode 9 和運行 iOS 11 的設備

iOS10 的 MPS 框架已實現支持 GPU 的快速 CNN 計算

支持在 iOS 設備上運行卷積神經網絡(CNN)的 API 已經加入到了 iOS 10 的 MPS (MetalPerformanceShaders)框架中。咱們如今能夠利用 GPU 實現快速的 CNN 計算,換句話說,最早進的深度學習技術已經能夠在單機上離線獨立運行。

須要傳入 MPSCNNConvolutionDataSource 對象。該對象負責加載權重。

咱們開始寫數據輸入類。因爲咱們的層都很是類似,因此咱們 DataSource 將爲全部層使用相同的類 - 可是每一個層都有本身的實例。代碼以下所示:

class DataSourceCNN: NSObject, MPSCNNConvolutionDataSource {
    let wName: String
    let bName: String
    let kernelWidth: Int
    let kernelHeight: Int
    let inputFeatureChannels: Int
    let outputFeatureChannels: Int
    let useLeaky: Bool
    let stride:Int
    let paramA:Float
    
    var wData: Data?
    var bData: Data?
    
    init(_ wName: String, _ bName: String, _ kernelWidth: Int, _ kernelHeight: Int,
         _ inputFeatureChannels: Int, _ outputFeatureChannels: Int,
         useLeaky: Bool = true, stride: Int = 1,paramA:Float=0.0) {
        self.wName = wName
        self.bName = bName
        self.kernelWidth = kernelWidth
        self.kernelHeight = kernelHeight
        self.inputFeatureChannels = inputFeatureChannels
        self.outputFeatureChannels = outputFeatureChannels
        self.useLeaky = useLeaky
        self.stride=stride
        self.paramA=paramA
    }
    
    func descriptor() -> MPSCNNConvolutionDescriptor {
        let desc = MPSCNNConvolutionDescriptor(kernelWidth: kernelWidth,
                                               kernelHeight: kernelHeight,
                                               inputFeatureChannels: inputFeatureChannels,
                                               outputFeatureChannels: outputFeatureChannels)
        if useLeaky {
            
            desc.setNeuronType(.reLU, parameterA: 0.0, parameterB: 0.0)
            
        } else {
            desc.setNeuronType(.none, parameterA: 0.0, parameterB: 0.0)
        }
        
        desc.strideInPixelsX=stride
        desc.strideInPixelsY=stride
        
        
        return desc
    }
    
    func weights() -> UnsafeMutableRawPointer {
        let ptr=UnsafeMutableRawPointer(mutating: (wData! as NSData).bytes)
        return ptr
    }
    
    func biasTerms() -> UnsafeMutablePointer<Float>? {
        
        return nil
    }
    
    func load() -> Bool {
        if let url = Bundle.main.url(forResource: name, withExtension: "bin") {
          do {
            data = try Data(contentsOf: url)
            return true
          } catch {
            print("Error: could not load \(url): \(error)")
          }
        }
        return false
    }
        
    func purge() {
        wData = nil
        bData=nil
    }
    
    func label() -> String? {
        return wName
    }
    
    func dataType() -> MPSDataType {
        return .float32
    }
}  
複製代碼

MPSCNNConvolutionDataSource 須要具備 load()purge() 函數。在這裏,咱們只需將上一步導出的二進制文件(例如,conv1.bin)加載到Data對象中便可。

要獲取此層的權重,該 weights() 函數將返回一個指向此 Data 對象的第一個元素的指針。假設咱們的層沒有偏置,因此 biasTerms() 能夠返回 nil.

如今,數據源被整理出來,咱們能夠開始構建圖:

let inputImage = MPSNNImageNode(handle: nil)

let scale = MPSNNLanczosScaleNode(source: inputImage,
                 outputSize: MTLSize(width: 416, height: 416, depth: 3))

let conv1 = MPSCNNConvolutionNode(source: scale.resultImage,
                                  weights: DataSource("conv1", 3, 3, 3, 16))

let pool1 = MPSCNNPoolingMaxNode(source: conv1.resultImage, filterSize: 2)

let conv2 = MPSCNNConvolutionNode(source: pool1.resultImage,
                                  weights: DataSource("conv2", 3, 3, 16, 32))

let pool2 = MPSCNNPoolingMaxNode(source: conv2.resultImage, filterSize: 2)

// ... and so on ...

guard let graph = MPSNNGraph(device: device, 
                             resultImage: conv9.resultImage) else {
  fatalError("Error: could not initialize graph")
}
複製代碼

咱們首先爲輸入圖像聲明一個節點,並將一個將該輸入圖像縮放到 416×416。接下來每一個層都使用 source 參數鏈接到前一個層。因此 scale 節點鏈接到 inputImageconv1 被鏈接到 scale.resultImage,等等。graph 自己是一個 MPSNNGraph 對象,並鏈接到網絡中最後一層的輸出 conv9

總結

Core ML 大大下降了開發者在蘋果設備上使用機器學習技術的門檻。蘋果制定了本身的模型格式,這樣當前主流機器學習模型經過轉換工具都能運用到 APP 當中。若是你是移動開發者,又看好機器學習,爲何不試一試呢?若是說前幾年是智能機時代,有可能將來幾年就是智能應用時代了。

深度學習侷限性

若是問題不能純粹地轉化爲一個映射問題時,深度學習的執行力就存在侷限性。(因爲深度學習更專一於一些基於數據驅動的映射問題,而事實上不管在視覺領域仍是在人工智能領域,不少問題並非映射問題。)

好比,深度學習解決問題時,是經過構建神經網絡架構實現的,這一過程過分依賴樣本。同時,咱們又不清楚深度學習具體如何解決問題、如何解釋解決問題的過程。所以,深度學習有一些和統計學習方法相同的頑疾:容易被一些方法陷害,好比灌入髒數據。從而使得深度學習作得很差。

例如,作一個巡邏機器人放在小區裏。開始時,業主和物業以爲這個巡邏機器人有趣,但後來以爲它沒用,不能解決他們的問題。他們就想這東西能幹什麼,而後提出了一個痛點需求:小區裏面貓屎、狗屎,若是沒有被及時清理,影響環境且容易被踩到。機器人能不能經過巡邏,找到狗屎,反饋給物業,讓保潔快速清理掉?

以這樣一個問題爲例,若是咱們用傳統的非深度學習的方法去作的話,可能要蒐集幾百、幾千張狗屎的照片,而後人工地去搜集它的顏色、形狀、以及紋理特徵,而後去調節分類器。咱們過去作人臉檢測、行人檢測,車輛檢測都是這麼作的,可能須要十幾年的時間才調出來一個還不錯的模型。可是深度學習模型一兩個月就能夠解決這個問題:咱們先用平臺去收集上萬張狗屎的照片,也許咱們再花上一兩個星期的時間,調調模型而後交給機器去訓練就行了,大概一兩個月也許就能夠部署這樣一個系統。

對於大數據來講這夠了,但和人相比仍是不夠。若是一個小孩踩了一次狗屎,基本上就不會踩第二次,也就意味着他基本上用一個樣本,幾秒鐘的時間就學完了狗屎檢測的問題。

深度學習缺少常識

好比說杯子不會懸在空中,它必定是在桌面上;狗屎通常不會在牆上,由於狗通常不會跑到牆上去拉屎,這些「常識」使得人在不須要不少樣本的狀況下,很精準的解決問題。好比行人檢測,人不會去樹上作檢測,那很奇怪。由於人知道,行人通常不會在樹上。好比汽車檢測,人不會在天上作汽車檢測。由於人知道,天上不會飛汽車。人其實都有「常識」,它使得人並不須要不少樣本,就能夠作很準確的判斷。

深度學習須要大量數據

自動駕駛其實你們知道,真正測試一個自動駕駛系統的行爲,不是靠這些 normal traffic(常規交通),而是靠什麼呢?靠不少邊界的case,靠不少不正常的 traffic data,好比小孩子忽然走到馬路,你可能一生很難碰到幾個,可是你就是要拿這些狀況去測試。可是你不可能用真實的數據,你不可能讓小孩真的去橫闖馬路,而後去測試你這個自動駕駛的系統,因此必定是用仿真的系統,去產生的不少的這種配製,而後去訓練去測試。這個是自動駕駛必需要走的路,那這個裏面實際上就是用大量的數據,可是這個數據是觸類旁通虛擬想象出來的。因此將來的話,有可能想象的數據會填補咱們對數據的缺失所帶來的掣肘,而後使你實際上effectively(有效的)是用小數據,可是你從 generate 不少大量的數據,使得你這個系統可以不斷的進化,去變得愈來愈聰明。

強化學習反饋時延好久

Alphago zero,你會發現它其實必定意義上來說沒有數據,由於它是徹底從零的狀態開始博弈,它徹底是左右博弈,去虛擬下無數盤棋。整個這個程序是用的深度學習增強化學習,在不斷的從虛擬的對決裏面去學習不少的經驗,最後達到一個很強大的能力,會接近棋盤真理。 zero data learning ,它是沒有用任何人類歷史的棋盤對決的數據,可是它又是大數據,爲何呢?由於它用不少虛擬的數據來學習,因此就是說,你就發現想象力使你在 zero data learningdata learning 之間好像有個蟲洞效應。實際上它們兩個之間距離是很是短的,不是咱們想象的差異那麼大。


四. 其餘資源

固然咱們也提供了 Slides 和錄屏,感興趣的同窗能夠經過下面的連接獲取:

王新義 - 自適應學習:機器學習在開心詞場中應用:錄屏 & Slides

冰霜 - 初探地圖類 App 後端那些事:空間索引算法:錄屏 & Slides

梅元剛 - iOS AI:錄屏 & Slides

五. 關於咱們

T 沙龍是一個非盈利的線下沙龍組織,咱們會按期舉辦 iOS 開發相關的線下沙龍活動,目的是促進 iOS 開發技術人員的線下面對面交流。沙龍經過採用閉門邀請制的方式,使參加者的技術水平和學習興趣儘量地接近,促進你們進行真正的交流。

六. 特別感謝

最後感謝本次沙龍的場地提供方 皚沐(上海)文化傳媒

相關文章
相關標籤/搜索