Categories
程式開發

是時候搞一個面向大數據和AI的新編程語言了


數據和AI正在成為很多公司的重要資產。其中,數據代表了公司在特定領域的積累,也是公司的護城河,AI能力輸出則代表了公司對數據資產的利用深度。二者不可分,光有數據沒有AI,就好比你只有原材料,卻無法加工產生價值;光有AI沒有數據,就好比你光有屠龍術,但是卻沒有龍給你屠。只有將二者作為一個整體,才能給一個公司帶來最大的價值。

大數據和AI融合存在的問題

數據和AI本身存在上下游關係,以及由於歷史發展而存在先後順序,這導致它們最終成為了兩個相對獨立的體系,帶來了兩個很大的問題:

大數據和AI的技術棧不同

大數據在語言層面以Java/Scala/SQL為主導,其中SQL是交互語言,Java/Scala則是大數據體系的構建語言。 AI則是以Python/C++ 為主導,其中Python為交互語言,C++為算法體系的構建語言。在面向的人群上,Java/Scala面向數據研發,SQL面向分析師、運營、產品。但是隨著SQL進一步發展,現在越來越多數據研發也開始直接使用SQL去解決自己的問題。 Python則更多面向算法。當然,分析師通常也會一些Python,而會Python的自然也會一些SQL。

從交互的複雜度而言:

C++ > Java/Scala > Python > SQL 

自然而然的,越是簡單的編程語言,就越容易變得流行,畢竟流行的本質就是使用人群擴大。只有門檻低的語言,才能提高使用人群的比例。所以SQL和Python慢​​慢分別發展成了大數據和算法的標準交互語言。實際上這些語言存在已久,只是因為各自的特點以及簡單性使得它們在新的時代得到了新的應用。

平台是隔離的

在很多公司,數據平台和AI平台通常是兩個平台,而且有時候連維護也是由兩個不同的團隊負責。這種平台之間的隔離造成了很大的互通問題,其中最大的問題就是數據互通。

完成一次對數據價值的開發,首先要對數據進行處理,處理後進一步交給AI去學習。而技術棧以及平台的不同,導致AI獲取數據的成本被提高了。用戶可能需要先去數據平台寫個數據處理腳本(譬如基於Spark等)提取數據,然後轉存到AI平台或者一個能被AI和數據平台共享的存儲上,然後再去AI平台寫Python或者調庫完成對數據的訓練。當然,隨著分工越來越細,其實用戶自己無法完成所有這些事情,他往往需要把這些步驟拆解然後下發到各個小的支持團隊裡去,然後各個小團隊進行排期,最終完成一整個鏈條的工作。無論對大公司還是小公司,這都會極大地消耗成本。

構建數據和AI平台依然困難

即使到今天,對很多公司來說,構建數據和AI平台依然很困難:需要了解無數的組件,需要有大量經驗且分別懂SQL、Java、Scala、Python的開發、分析師、算法,或者需要了解各種雲產品並做組合以達到自己想要的效果。所有這些困難堆疊起來就會導致極其高昂的開發成本。同時,由於IT特有的屬性,即使做了這些投入,可能依然滿地是坑。面對底層無數存儲,如何做一個有效的權限控制就已經讓很多開發撓頭了。

而且,數據和AI應該是普惠的。在筆者看來,數據和AI不應該僅僅局限在數據科學家手裡(包括分析師和算法),任何一個產品、運營,甚至任何一個有權訪問數據的人,都應該可以對這些數據進行價值開發。這種開發小到只是查看,或者導出成一個Excel,或者對外提供一個查詢接口,大到提供一個精準的推薦系統,都應該能比較容易地完成。

我們需要一個面向大數據和AI的新語言

前面說了這麼多,無非是想告訴大家:

  1. 大數據和AI需要融合成一個平台;
  2. 構建這個平台的成本應該要降下來;
  3. 數據和AI的使用門檻要降下來,人人都應該能夠利用數據給公司創造價值。

那怎麼才能實現上面這三個訴求呢?單單從平台層面很難解決這個問題,這個時候我們就需要從語言層面著手了。我們需要一個能夠更好地融合大數據和AI的編程語言,它必須具備以下特點:

  1. 語言要足夠簡單,產品和運營也能很容易學會;
  2. 能夠同時完美滿足大數據和算法的需求,打通兩者互通的數據管道;
  3. 語言的執行引擎要是分佈式的,並具備強大的硬件資源管理能力,能夠滿足海量數據計算需求;
  4. 必須保護現有的投資,包括能夠復用現有的大數據生態和AI生態。

Python對大多數人還是有一定難度,而且無法很好地整合大數據生態。 SQL簡單,但是無法滿足算法的複雜需求且難以利用AI領域大量生態。

我們希望用一個語言完成大數據批流處理以及AI的訓練和預測。我們希望在語言層面內建數據安全機制,解決無數底層存儲的訪問控制問題,讓開發不再撓頭。我們還希望這個語言內置的功能易於擴展,且能夠充分利用大數據和AI的構建語言Java/Scala/C++做擴展,因此這個語言的執行引擎應該具有強大的插件化能力。

目前正在朝著這個目標努力的編程語言有SQLFlow和MLSQL。

SQLFlow

SQLFlow的初衷是為了解決成千上萬分析師既要操作數據又要使用AI,往往需要在兩個系統甚至更多系統之間切換導致工作效率低的窘境。

SQLFlow對SQL做了部分語法擴展,用戶可以在SQL中加入算法的描述。

一個典型的語法如下:

-- SQL 部分
SELECT * FROM 20newsgroups.train 
-- 算法描述部分
TO TRAIN DNNClassifer
WITH hidden_units = [10, 10], n_classes = 3, EPOCHS = 10
COLUMN sepal_length, sepal_width, petal_length, petal_width
LABEL class 
INTO sqlflow_models.my_dnn_model;

其基本邏輯是提供了一個基於Go開發的Service,用於接收發往後端存儲的SQL,然後對SQL進行解析並提取出數據描述部分和算法描述部分。

SQLFlow會將數據描述部分(SQL)發送給後端存儲引擎,並把後端返回的數據餵給算法部分做訓練;同時根據算法描述自動生成Python代碼,然後提交給特定的算法引擎來完成訓練,最後將結果保存回存儲引擎。目前後端支持MySQL、Hive、MaxCompute等,算法引擎支持 TensorFlow、PyTorch。

不過筆者個人認為,SQLFlow需要研發去適配眾多存儲後端,同時還要針對各個算法引擎做支持,並且語法擴展部分目前還不夠靈活,只能使用SQLFlow已經實現好的一些算法,縮小了SQLFlow的覆蓋人群,譬如專業的算法工程師應該不大會去使用它。

MLSQL

MLSQL一開始的出發點也是為了解決算法的普惠性,希望不僅僅是AI人才能用上算法,工程師、分析師和產品運營都應該能用起來。

和SQLFlow只是簡單擴展SQL不同的是,MLSQL是基於SQL語法設計的全新的編程語言。

對於工程師或分析師來說,可以完全使用類SQL語法來實現訓練和預測,這一點上MLSQL和SQLFlow是相同的:

select  * from  20newsgroups as 20newsgroups;
train 20newsgroups 
as RandomForest.`/ai_model/rf` 
where keepVersion="true" 
and   evaluateTable="tfTable_test"
and   `fitParam.0.featuresCol`="value"
and   `fitParam.0.labelCol`="label_num"
and   `fitParam.0.maxDepth`=“2";

系統會自動生成Spark/Python代碼完成訓練。

而對於專業的算法工程師,MLSQL支持使用內嵌Python進行訓練(這一點目前在SQLFlow官方文檔中沒有體現):

!python env "PYTHON_ENV=source activate dev";
!python conf "schema=st(field(count_vect,binary),field(tfidf_transformer,binary),field(nb,binary))";
load delta.`ai_data.20newsgroups` as 20newsgroups;
-- 提取数据中的分类,因为隐含在url里。
select *, split(file,"/")[4] as label from 20newsgroups as 20newsgroups;
--我们可视化下数据分布
select label,value from 20newsgroups as 20newsgroups;

!ray on 20newsgroups '''
import pandas as pd
import plotly.express as px
from plotly.io import to_html
from vega_datasets import data
from pyjava.api.mlsql import PythonContext,RayContext
ray_context = RayContext.connect(globals(),None)
data = list(ray_context.collect())
df = pd.DataFrame(data, columns=data[0].keys())
from sklearn.feature_extraction.text import CountVectorizer
count_vect = CountVectorizer()
X_train_counts = count_vect.fit_transform(df.value)
from sklearn.feature_extraction.text import TfidfTransformer
tfidf_transformer = TfidfTransformer()
X_train_tfidf = tfidf_transformer.fit_transform(X_train_counts)
from sklearn.naive_bayes import MultinomialNB
print("这是进行训练的过程")
clf = MultinomialNB().fit(X_train_tfidf, df.label)
docs_new = ['God is love', 'OpenGL on the GPU is fast']
X_new_counts = count_vect.transform(docs_new)
X_new_tfidf = tfidf_transformer.transform(X_new_counts)
print("这是进行预测的过程。")
predicted = clf.predict(X_new_tfidf)
for doc, category in zip(docs_new, predicted):
    print('%r => %s' % (doc, category))
# 最好是能讲数据序列化后上传到模型仓库
# 不过我们这里就先保存成表
import pickle
context.build_result([{"count_vect":pickle.dumps(count_vect),"tfidf_transformer":pickle.dumps(tfidf_transformer),"nb":pickle.dumps(clf)}])
''' named model_data;
-- 把三个模型保存到数据湖
save overwrite model_data as delta.`ai_model.20newsgroups` where overwriteSchema="true";

上述代碼從數據湖中提取數據,使用SQL進行簡單處理,然後使用Python做了一個文本分類訓練,最後把模型保存回數據湖中。

從語言層面來講,MLSQL有點三不像:

  1. 有命令行的東西
  2. 有類SQL的東西
  3. 有Python

我們相信部分任務僅僅用類似命令行就能解決,大部分僅僅用SQL就能解決,只有非常少的一部分才需要動手寫Python。因此MLSQL將SQL作為一等公民,命令行是SQL的一個語法糖,Python作為內嵌語言,也是為了盡可能讓語言的使用更簡單。

MLSQL底層腳本執行引擎採用的是Spark,這意味著MLSQL可以充分利用現有的大數據生態,目前已經支持市面上大部分存儲,包括數倉、關係型數據庫、數據湖、分佈式文件系統等,並且可以利用Spark自身強大的算力完成混算。而基於MLSQL另外一個子項目PyJava開發的Python執行引擎,則使得用戶可以充分利用Python的生態體系。另外,MLSQL也對分佈式機器學習框架Ray做了深度的集成,並且支持插拔使用。

MLSQL底層架構如下圖所示,使用者可以用MLSQL同時完成批處理、AdHoc計算、流式計算、機器學習等諸多任務。

是時候搞一個面向大數據和AI的新編程語言了 1

不要被架構圖的複雜度嚇到,MLSQL執行引擎的部署和一個普通的Spark應用沒有任何區別。

除此之外,MLSQL還有諸多特性,譬如:

  1. 在語言層面內置了到列級別的權限訪問控制;
  2. 插件化內核,核心功能均採用內置插件完成,現在也有不少官方插件;
  3. 支持在腳本中使用Java/Scala語言書寫UDF/UDAF;
  4. 支持include語法,強化了SQL工程和復用能力。

以上兩個項目的開源地址如下:

結語

當下編程語言越來越趨向領域化,最終都是要讓合適的語言為合適的領域助力。我們相信,大數據和AI的蓬勃發展,必然需要一個更加為之量身定制的語言。以SQLFlow、MLSQL為代表的這類語言的誕生正是順應了這個趨勢。