2015-11-10 4 views
1

Я реализовал алгоритм smo в python. Поскольку я просто делаю это как практику, поэтому я не использовал какую-либо научную вычислительную библиотеку, такую ​​как numpy и scipy. Я просто хочу, чтобы он работал правильно. Но когда я тестирую свой код на diabetes, он работает в течение недели! Я проверил свой код много раз, и я также обнаружил некоторые ошибки. но после исправления этих ошибок код все еще работает слишком медленно. Я не знаю, есть ли какие-то ошибки, которые я не проверил, или smo просто изначально настолько медленным.Почему мой smo работает так медленно?

. Есть ли некоторые распространенные ошибки, которые могут замедлить работу кода? Я написал свою программу, ссылаясь на псевдокод smo paper Большое спасибо.

Ниже приведен мой код.

#encoding=utf8 
import math 
import random 

class SVM(object): 
    def __init__(self, dataset, target, C=0.001, tolerance=0.001): 
     self.dataset=dataset 
     self.target=target 
     self.C=C 
     self.tolerance=tolerance 
     self.alpha=[0.0 for i in range(len(dataset))] 
     self.E={} 
     self.b=0.0 
     self.w=[0.0 for i in range(len(dataset[0]))] 

    def train(self): 
     numChanged=0 
     exampleAll=1 

     trainset_size=len(self.dataset) 
     iter=0 
     while numChanged > 0 or exampleAll: 
      numChanged=0 
      if exampleAll: 
       for i in range(trainset_size): 
        numChanged+=self.examineExample(i) 
       iter+=1 
      else: 
       for i in range(trainset_size): 
        if self.alpha[i] > 0 and self.alpha[i] < self.C: 
         numChanged+=self.examineExample(i) 
       iter+=1 
      if exampleAll: 
       exampleAll=0 
      elif numChanged == 0: 
       exampleAll=1 
      print "iter", iter 
      print "alpha", "\t".join([str(i) for i in self.alpha]) 
      print "target", "\t".join(self.target) 
     for j in range(len(self.trainset[0])): 
      for i in range(trainset_size): 
       self.w[j] +=self.alpha[i]*int(self.target[i])*float(self.dataset[i][j]) 

    def examineExample(self, i2): 
     print "in examineExample", i2 
     print "alpha", "\t".join([str(i) for i in self.alpha]) 
     alpha2=self.alpha[i2] 
     y2=int(self.target[i2]) 
     e2=self.calculateE(i2) 
     r2=e2*y2 
     print "r2", r2 

     if r2 < -self.tolerance and self.alpha[i2] < self.C or r2 > self.tolerance and self.alpha[i2] > 0: #i2违反了kkt条件 
      i1=self.select_i1(i2,e2) 
      if self.takeStep(i1, i2): 
       return 1 
      else: 
       all_sample_index=[i for i in range(len(self.dataset)) ] 
       random.shuffle(all_sample_index) 
       for k in range(len(all_sample_index)): 
        i1=all_sample_index[k] 
        if self.alpha[i1] > 0 and self.alpha[i1] < self.C: 
         if self.takeStep(i1, i2): 
          return 1 

       random.shuffle(all_sample_index) 
       for k in range(len(all_sample_index)): 
        i1=all_sample_index[k] 
        if self.takeStep(i1,i2): 
         return 1 
     return 0 

    def takeStep(self, i1, i2): 
     print "in takeStep", i1, i2 
     if i1==i2: 
      return 0 
     alpha1=self.alpha[i1] 
     y1=int(self.target[i1]) 
     e1=self.calculateE(i1) 

     alpha2=self.alpha[i2] 
     y2=int(self.target[i2]) 
     e2=self.calculateE(i2) 

     s=y1*y2 

     if y1 != y2: 
      L=max(0, alpha2-alpha1) 
      H=min(self.C, self.C+alpha2-alpha1) 
     if y1== y2: 
      L=max(0, alpha2+alpha1-self.C) 
      H=min(self.C, alpha2+alpha1) 
     if L==H: 
      return 0 
     k11=self.kernel(i1, i1) 
     k12=self.kernel(i1, i2) 
     k22=self.kernel(i2, i2) 
     eta=k11+k22-2*k12 

     if eta > 0: 
      self.alpha[i2]=alpha2+y2*(e1-e2)/eta 
      if self.alpha[i2] < L: 
       self.alpha[i2]=L 
      if self.alpha[i2] >H: 
       self.alpha[i2]=H 

      print "abs", abs(self.alpha[i2] - alpha2) 
      if abs(self.alpha[i2] - alpha2) < 0.00001 
       return 0 

      self.alpha[i1]=alpha1+s*(alpha2-self.alpha[i2]) 

      b1=self.b-e1-y1*(self.alpha[i1]-alpha1)*self.kernel(i1,i1)-y2*(self.alpha[i2]-alpha2)*self.kernel(i1,i2) 
      b2=self.b-e2-y1*(self.alpha[i1]-alpha1)*self.kernel(i1,i2)-y2*(self.alpha[i2]-alpha2)*self.kernel(i2,i2) 

      print "two old alpha", alpha1, alpha2 
      print "two alpha", self.alpha[i1] ,self.alpha[i2] 
      if self.alpha[i1] >0 and self.alpha[i1] < self.C and self.alpha[i2] > 0 and self.alpha[i2] < self.C: 
       print "two b", b1, b2 
      if self.alpha[i1] >0 and self.alpha[i1] < self.C: 
       self.b=b1 
      elif self.alpha[i2] > 0 and self.alpha[i2] < self.C: 
       self.b=b2 
      else: 
       self.b=(b1+b2)/2 
      self.E[i2]=self.calculateE(i2) 
      self.E[i1]=self.calculateE(i1) 
      return 1 
     else: 
      return 0 

    def select_i1(self, i, Ei): 
     maxK=-1; 
     maxDeltaE=0.0 
     Ej=0 

     self.E[i]=Ei 

     for k in range(len(self.dataset)): 
      if self.alpha[k] > 0 and self.alpha[k] < self.C: 
       Ek=self.calculateE(k) 
       deltaE=Ek-Ei 
       if abs(deltaE) > maxDeltaE: 
        maxK=k 
        maxDeltaE=deltaE 
        Ej=Ek 

     if maxK != -1: 
      return maxK 
     else: 
      j=i 
      while j == i: 
       j=random.randint(0, len(self.dataset)) 
      return j 

    def calculateE(self, i): 
     f_x=0.0 
     trainset_size=len(self.dataset) 
     for k in range(trainset_size): 
      f_x+=(self.alpha[k]*int(self.target[k])*self.kernel(k,i)) 
     f_x+=self.b 
     e_x=f_x-float(self.target[i]) 
     return e_x 


    def kernel(self, i, j): 
     return sum([float(self.dataset[i][k])*float(self.dataset[j][k]) for k in range(len(self.dataset[i]))]) 

    def test(self, testset, testset_target): 
     precision=0.0 
     correct=0 
     for k in range(len(testset)): 
      sample =testset[k] 
      pred_value=0.0 
      for i in range(len(sample)): 
       pred_value+=self.w[i]*sample[i] 
       pred_value+=self.b 

      if pred_value >= 0: 
       label=1 
      else: 
       label=-1 
      if testset_target[k] == label: 
       correct+=1 

     precision=correct/(float(len(testset_target))) 

     return precision 

def read_libsvm_format_file(dataset_filename): 
     dataset_file=file(dataset_filename,'r') 
     dataset_label=[] 
     dataset=[] 
     for line in dataset_file: 
      splitted=line.strip().split() 
      dataset_label.append(splitted[0]) 
      sample=[] 
      for i in range(1,len(splitted)): 
       index_value=splitted[i].split(":") 
       sample.append(index_value[1]) 
      dataset.append(sample) 
     return dataset, dataset_label 

if __name__ == "__main__": 
     dataset, target =read_libsvm_format_file('diabetes') 

     trainset_size=500 
     index=range(len(dataset)) 
     random.shuffle(index) 
     trainset=[ dataset[index[i]] for i in range(trainset_size) ] 
     trainset_target=[ target[index[i]] for i in range(trainset_size) ] 

     testset=[ dataset[index[i]] for i in range(trainset_size, len(index)) ] 
     testset_target=[ target[index[i]] for i in range(trainset_size, len(index)) ] 

     svm=SVM(dataset, target) 
     svm.train() 
+1

Попробуйте использовать библиотеку 'cProfile' для поиска узких мест в вашем коде. – asimoneau

ответ

3

Общие проблемы

К сожалению, наиболее распространенная ошибка в научном программировании питона, который вызывает код для запуска медленно есть ... использовать чистый питон. Петли Python медленны, периоды. And by slow I mean extremely slow. Даже если предположить, что Eveything совершенно правильно, вы будете в конечном итоге с крайне медленным оптимизатора, если вы не выполните одно из следующих действий:

  • переключатель в Cython (дает доступ к красоте типизированных петель)
  • использования numpy (базовая цифровая библиотека)
  • попробуйте запустить код через pypy (это не библиотека, она отличается переводчик, быстрее один)

Bugs
  1. Существует нет : после

    if abs(self.alpha[i2] - alpha2) < 0.00001 
    

    Так что не будет работать даже.

  2. Далее, после ее фиксации и подножка на diabetes он выходит из строя

    r2 -0.999256460902 
    in takeStep 658 2 
        Traceback (most recent call last): 
         File "a.py", line 218, in <module> 
         svm.train() 
         File "a.py", line 26, in train 
         numChanged+=self.examineExample(i) 
         File "a.py", line 55, in examineExample 
         if self.takeStep(i1, i2): 
         File "a.py", line 79, in takeStep 
         e1=self.calculateE(i1) 
         File "a.py", line 160, in calculateE 
         f_x+=(self.alpha[k]*int(self.target[k])*self.kernel(k,i)) 
         File "a.py", line 167, in kernel 
         return sum([float(self.dataset[i][k])*float(self.dataset[j][k]) for k in range(len(self.dataset[i]))]) 
        IndexError: list index out of range 
    

    , которое вызвано вашей недействительных функции чтения. libsvm (svmlight) dataformat является редким, поэтому некоторые из измерений могут отсутствовать - ваш код предполагает, что это не так.

  3. Вы даже читать данные в виде строк

    index_value=splitted[i].split(":") 
    sample.append(index_value[1]) 
    

    Это должно быть (после того, как вы предварительно выделить свои списки примеров, чтобы они достаточно большие, чтобы соответствовать данным, или использовать defaultdicts sample = defaultdict(lambda: 0)).

    index_value=splitted[i].split(":") 
    sample[int(index_value[0])-1] = float(index_value[1]) 
    

    То же самое касается считывающих этикеток. Следовательно, у вас есть десятки полностью избыточных типов конверсий в вашем коде (все ваши текущие вызовы float() и int() избыточны).

  4. Существует также ошибка, в окончательном здании w:

    [...] self.trainset [...] // you do not have a "trainset" field in SVM 
    
  5. В тестировании кода вы добавляете перехватывать (b) несколько раз

    for i in range(len(sample)): 
         pred_value+=self.w[i] * sample[i] 
         pred_value+=self.b 
    

    в то время как он должен быть

    for i in range(len(sample)): 
         pred_value+=self.w[i] * sample[i] 
    pred_value+=self.b 
    
  6. I не было бы удивлено, если бы обнаружилось также много ошибок в самом SMO, что может привести к тому, что алгоритм не сходится вообще, но на данный момент мне удалось найти выше.

После исправления

после фиксации все выше, удаляя все сообщения отладки печати и работает с pypy я получаю следующие модели:

[-0,7725132490683443, -2,8232379861128907, +0,5166865781499452, 0,1494369704938019, 0,1533317981122747, -1.9500615428909012, -0.7957828887451327, -0.12523832631571777]

в то время как scikit учиться дает

[0.77296251 2.82387247 -0.51692311 -0.14987696 -0.15312237 1,94999242 0,79593224 0.12527931]

Так, чтобы подписать это та же модель.

При использовании C=1 оба кода в конечном итоге обучение заданной точности 0.776041666667

Сравнение времени

  • с использованием кода [с литьем типа] и чистый питона

реального 9М11. 017s

  • с использованием кода [с литьем типа] и pypy

реальные 0m47.033s

  • с использованием кода [без приведения типа] и pypy

реальный 0m40.215S

  • использованием scikit-Learn SMO (код C)

реальные 0m0.338s

Заключительные замечания

Оказывается, что большинство ваших ошибок являются расположенных в утилитах для чтения данных. Кроме того, как было сказано в начале - «классический» интерпретатор python имеет чрезвычайно медленные петли, поэтому вам нужно либо использовать pypy (и отсутствие поддержки для многих библиотек), либо cython (и более сложную разработку), либо, по крайней мере, числовые библиотеки, такие как numpy и scipy.

+0

Большое спасибо за проверку моей программы. Поскольку я загрузил неправильную версию своей программы, некоторые очевидные ошибки, которые заставляют ее не запускаться, не существуют в моей окончательной версии. Но ошибка №3 - это действительно моя ошибка. Я не преобразовал строку в float, когда прочитал файл данных. Что касается ошибки № 2, я должен извиниться перед вами, поскольку я вставляю другой URL-адрес набора данных Diabetes, в то время как на самом деле я использую [это в libsvmtools] (https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/ datasets/binary/diabetes) в моем тесте – ningyuwhut

+0

, когда я исправляю вышеуказанные ошибки в моей программе, упомянутой выше, и использую pypy для запуска моей программы python, программа все еще работает в течение длительного времени. Но когда я использую sklearn для тестирования, программа дает результат менее 1 секунды. Я думаю, что у самих алгоритмов smo есть некоторые erros, и я проверю это. Благодарю. – ningyuwhut

Смежные вопросы