最近看到一篇關於poi的論文,把poi各個類別通過鄰接關係利用Word-embedding訓練成了詞向量,這樣原本屬於不同大類下的子類但是功能很相近的類別就在詞向量空間裡顯得比較近,更符合實際情況。相比於之前的分開看各種poi類別比例,這樣可以更好地表達出城市內的poi配置情況。
論文提要
Liu K, Yin L, Lu F, et al. Visualizing and exploring POI configurations of urban regions on POI-type semantic space[J]. Cities, 2020, 99: 102610.
主要技術路線如下:
圖片來自論文
選擇深圳市地鐵站700m範圍內poi作為研究數據
圖片來自論文
參考Skip-gram模型(Word-embedding)的一種,把每個poi類別當做輸入(one-hot編碼處理),並給定一個閾值範圍(500m),統計範圍內的所有poi比例,作為輸出
圖片來自論文
根據各類別的詞向量進行分析
圖片來自論文
他們還做了一個下面這樣的交互頁面,http://hpcc.siat.ac.cn/liuk/POI_configuration_en/index.html
圖片來自論文簡單復現
ok,介紹就到這裡,我們拿上海的poi數據簡單復現一下
「step1」篩選上海地鐵站700m內poi數據這裡是通過geopandas對每個地鐵站建立緩衝區,然後利用sjoin與各個poi點做相交,判斷是否在緩衝區內,這裡注意做緩衝區需要先把地理坐標系這種轉換為平面坐標系,做完緩衝區我們再換回去
#讀取poi數據data=pd.read_csv(r"E:\data\上海_201812_WGS84_poi_type.csv")data['geometry']=data.apply(lambda x:geometry.Point(x.ln,x.lat),axis=1)data=geopandas.GeoDataFrame(data)data.crs = pyproj.CRS.from_user_input('EPSG:4326')#讀取上海地鐵站數據urban=pd.read_csv("E:/data/shanghai_urban.csv")urban['geometry']=urban.apply(lambda x:geometry.Point(x.lon,x.lat),axis=1)urban=geopandas.GeoDataFrame(urban)urban.crs = pyproj.CRS.from_user_input('EPSG:4326')#設置緩衝區範圍distance=700urban=urban.to_crs(crs="EPSG:2385")urban['geometry']=urban.buffer(distance)urban.set_geometry('geometry',inplace=True)#設置完緩衝區後再換回來urban=urban.to_crs(crs="EPSG:4326")#判斷是否與地鐵接駁範圍相交result=geopandas.sjoin(data,urban,how="left",op='intersects')result.to_csv('result.csv')result.set_geometry('geometry',inplace=True)##保存接駁距離內poi數據urban_in=result[pd.notna(result['name_right'])]urban_in.to_csv(r'E:\data\shanghai_poi_within_urban.csv')
這裡統計下來,上海poi在軌道站700m範圍內的佔「61.85%」
「step2」篩選每個poi點周圍500m內的poi數據,統計比例
此時數據裡有141類poi了,這裡我們應該對每一個poi點都做500m的緩衝區,找到落在該緩衝區內的141個子類的比例
這一步的話相當於是做99萬+的緩衝區實在搞不動,要麼多做幾次很費時間,要麼做一次又沒內存,反正只是為了試驗一下,就把每一類抽了1%,最後得到9962條數據
data=pd.read_csv(r"E:\data\shanghai_poi_within_urban.csv")type_list=data['type_id'].unique().tolist()def sample_data(type_id): con=data['type_id']==type_id tempdata=data.loc[con,:].copy() outdata=tempdata.sample(frac=0.01,random_state=1) return outdataresult_list=[]for i in range(0,len(type_list)): result_list.append(sample_data(type_list[i]))result=pd.concat(result_list,axis=0)result.to_csv(r'shanghai_poi_within_urban_sample.csv')
接著對經過抽樣的數據中每一個poi點都做500m的緩衝區,這裡做緩衝區同step 1,不再贅述
得到下圖結果
然後統計不同類別的數目
table=pd.pivot_table(data=data,index=['id'],values['index_right'],columns=['type_id_right'],aggfunc='count')table=table.reset_index()table=table.fillna(value=0)
轉換成比例
data=pd.read_excel(r'C:\users\fff507\Desktop\data_poi_procssed.xlsx')data_value=data.iloc[:,1:].valuesdf1=pd.DataFrame(data_value)df2=df1.sum(axis=1)# 按行相加new_data=df1.div(df2, axis='rows')
「step3」利用skip-gram訓練,得到各類別對應的詞向量
skip-gram原理
把中心詞的onehot編碼當做輸入,上下文裡的詞出現的概率做輸出,以此來進行訓練,其實真正想要的是中間的隱藏層權重,也就是我們想要的對應於每個詞的詞向量。
我們這個背景裡,中心詞類似於每一個poi,上下文類似於緩衝區內的poi基本原理其實也很簡單,核心就是兩個矩陣,看一下下面我畫的圖,就是W1,W2的問題,他們也是我們最終想要的
小周親手畫的
那麼輸入就是某個poi的one-hot編碼,輸出就是對應於141類的概率「但其實我們想要的是中間隱藏層的權重,W1,W2」
我直接按照上圖原理搞了個最簡單的
def weight_variable(shape,name_w): initial = tf.truncated_normal(shape, stddev=0.1,name=name_w) return tf.Variable(initial)
X = tf.placeholder(tf.float32, [None, input_size],name='x')W1 =weight_variable([input_size,embedding_size],name_w='W1')v_c= tf.matmul(X, W1)W2=weight_variable([embedding_size,input_size],name_w='W2')M=tf.matmul(v_c, W2)Y = tf.placeholder(tf.float32, [None,output_size],name='y')loss=tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logit_v2(logits=M, labels=Y))train_op =tf.train.AdamOptimizer(learning_rate=learning_rate).minimize(loss)correct_prediction = tf.equal(tf.argmax(M, 1), tf.argmax(Y,1))accuracy = tf.reduce_mean(tf.cast(correct_prediction,"float"))saver = tf.train.Saver(tf.global_variables())
最後測試集準確率在75.78%,141類也不算很差吧
訓練結果
獲取W1矩陣,也即詞向量矩陣(141*50)
這個我看了有人說W1和W2都可以本質是一樣的,也有人說應該取W1,官方取得是W1,我們這裡也取W1好了
from tensorflow.python import pywrap_tensorflow#首先,使用tensorflow自帶的python打包庫讀取模型model_reader =pywrap_tensorflow.NewCheckpointReader(r"model_emb/model.ckpt")#然後,使reader變換成類似於dict形式的數據var_dict = model_reader.get_variable_to_shape_map()W1=model_reader.get_tensor('W1')#141*50
「step4」降維後做一些可視化分析
# 降維到二維pca = PCA(n_components=2)pca.fit(W)# 輸出特徵值print(pca.explained_variance_)#輸出特徵向量print(pca.components_)# 降維後的數據W_new = pca.transform(W)result=pd.DataFrame(W_new)result.to_excel('poi_2d_wight.xlsx',index=False)
poi2維分布
由於內存限制本文只是實驗性的拿差不多1w(1%)試驗,難免有偏差結果看起來有些應該在一起的沒有在一起(比如公交站和地鐵站),不過看這個論文裡的結果挺好的
ok,我們挑幾個地鐵站來看看poi配置區別那就挑黃渡理工,同濟,交大,復旦吧
周邊poi配置
下面這個當圖例
我是圖例
黃渡理工獨樹一幟,而同濟和交大地鐵站周邊分布最為相似,都有較多的生活服務,科教文化,培訓結構,復旦大學地鐵站是18號線在建,所以相對偏少,但是在事務所那裡復旦大學地鐵站周圍高於其他。
「ok,只是想試驗下embedding,下期更新可能是兩三四五六個星期了」
不用讚賞,點讚轉發是最吼的