要做本科畢業論文,我在一篇論文裡面學習一個入門級的機器學習演算法,基於最近鄰規則,為其計算一個訓練集一致子集,但是我根據論文實現的演算法,速度遠慢於論文中的數據。 (論文名稱:Fast Nearest Neighbor Condensation for Large Data Sets Classification)

在Forest Cover Type數據集中(54個屬性,58萬條數據),論文中FCNN1演算法的運算速度是3750秒,但是我實現的演算法,起碼慢它十倍。(數據集地址:Forest CoverType)

代碼地址:https://github.com/If-Wu/ML-FCNN

新手入門,代碼寫的有點亂。

我在Python中使用numpy的歐式距離計算,58萬次大概需要4秒,子集大小58000,理論上要58000*4秒。我使用了三角不等式減少距離計算,但速度還是遠遠慢於論文中的數據。這可能是哪裡出現了問題?要優化有什麼辦法?多進程?

論文中電腦配置:All the experiments were executed on a Pentium IV 2.66-GHz-based machine with 1 Gbyte of main memory under the Windows operating system.

我使用的是自己的筆記本電腦:


可以用gpu加速?可能你的實現沒有他的好,可以用sklearn框架訓練你的數據,看看是否加快,好檢查你寫的演算法


def f_cnn_1(train_set, train_label):
# 訓練集的所有下標
set_t = [i for i in range(len(train_set))]
# 初始化最近鄰列表 nearest[5] = 120, 點5在集合S中的最近鄰是120
nearest = [-1] * len(train_set)
# 子集S
set_s = []
# △S, 初始化是所有類的質心
set_si = get_centroid_index(train_set, train_label, set_t)

loop_number = 0

calc_distance = CalcDistance(train_set)
# 主循環
while len(set_si) &> 0:
print(len(set_si))
loop_number += 1

# S∪△S
set_s.extend(set_si)

# 初始化rep數組,集合S中當前元素的代表元素
# rep[325] = ? 不能單純的使用列表, 使用字典好一些
rep = dict()
for i in set_s:
rep[i] = None

# 集合 T - S
t_s = [item for item in set_t if item not in set_s]
start = time.time()
p_data = train_set[set_si]
for q in t_s:
p_distance = np.linalg.norm(p_data - train_set[q], axis=1)
p_distance_min_index = np.argmin(p_distance)
p_distance_min = p_distance[p_distance_min_index]

n_q_q_d = calc_distance.calc(nearest[q], q)
if n_q_q_d &> p_distance_min:
nearest[q] = set_si[p_distance_min_index]
n_q_q_d = p_distance_min
# 更新 T-S 集合中點在S中的最近點
# for p in set_si:
# if n_q_q_d &> calc_d(p, q, train_set):
# nearest[q] = p
# n_q_q_d = calc_d(nearest[q], q, train_set)
# l(q) != l(nearest[q]) ---&> 判斷該點是否處在 Voren集合中
# 如果是,則判斷 d(nearest[q],q) &< d(nearest[q], rep[nearest[q]]) # FCNN1 中,代表元素挑選當前原則:為每個S中的元素p,挑選以p為最近鄰 且 以標籤不同類集合中,里p最近的一個 # nearest[q]是 set_S中的點,rep[nearest[q]]是T-S中的點 if train_label[q] != train_label[nearest[q]]: if n_q_q_d &< calc_distance.calc(nearest[q], rep[nearest[q]]): rep[nearest[q]] = q end = time.time() print("Use Time 1: ", end - start) set_si.clear() for p in set_s: if rep[p] is not None: set_si.append(rep[p]) return set_s class CalcDistance: def __init__(self, train_set): self.train_set = train_set @lru_cache(maxsize=50000) def calc(self, p, q): distance = float("inf") if p != -1 and q != -1: distance = np.linalg.norm(self.train_set[p] - self.train_set[q]) # d_cache[key] = distance return distance

對原始版本稍微做下改進,主要是將循環調用numpy計算距離改為一次計算出來.避免了一些不必要的計算.常規的距離計算加上了緩存,可以免去一些重複計算.

在我的機器上首次和第二次循環分別從39秒,42秒減到了6.5秒,11秒.


樓主不要優化了 直接上開源庫sklearn,樓主恐怕不是計算機科班出身,自己實現的效率太低

sklearn_kNN?

scikit-learn.org

代碼如下案例

X = [[0], [1], [2], [3]]
y = [0, 0, 1, 1]
from sklearn.neighbors import KNeighborsClassifier
neigh = KNeighborsClassifier(n_neighbors=3)
neigh.fit(X, y)

print(neigh.predict([[1.1]]))

print(neigh.predict_proba([[0.9]]))


code please.

沒代碼鬼知道怎麼分析。看樣子你參考的論文是針對有效計算資源的場景,和你的環境差別不大,慢個兩三倍可能在意料之中,慢十倍應該是你的實現有問題。


100萬次×200維,暴力算一遍我這是60毫秒,單線程主頻2.1G,C++開O2,可能是你代碼寫錯了


就像上面的回答。

你可以熟悉一下 sklearn怎麼實現了的,然後對比一下速度。然後改善自己的演算法。

並且這個過程也可以寫到畢業論文中。

多好~


有一個演算法叫 k-d tree,可以把全部 計算的時間從 [公式] 壓縮至 [公式] ,論文可能採用了這種方法。

其中, [公式] 為 feature 數,[公式] 為訓練樣本量, [公式] 為測試樣本量。

有興趣可以嘗試實現。


如果你不想使用這些奇怪的演算法,也有一些解決方案。

你可以使用 tuna 做個性能分析,如果瓶頸在循環上,可以用 numba 給熱點代碼加個 JIT, 或者把循環改為 numpy 式的。(這麼慢,問題大概率就是循環)

還有,你可以把測試樣本用 multiprocessing 庫並行處理。(雖然能大幅提速,但這對論文中的 CPU 不公平)


推薦閱讀:
相关文章