grid.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. #!/usr/bin/env python
  2. import os, sys, traceback
  3. import Queue
  4. import getpass
  5. from threading import Thread
  6. from string import find, split, join
  7. from subprocess import *
  8. # svmtrain and gnuplot executable
  9. is_win32 = (sys.platform == 'win32')
  10. if not is_win32:
  11. svmtrain_exe = "../svm-train"
  12. gnuplot_exe = "/usr/bin/gnuplot"
  13. else:
  14. # example for windows
  15. svmtrain_exe = r"..\windows\svm-train.exe"
  16. gnuplot_exe = r"c:\tmp\gnuplot\bin\pgnuplot.exe"
  17. # global parameters and their default values
  18. fold = 5
  19. c_begin, c_end, c_step = -5, 15, 2
  20. g_begin, g_end, g_step = 3, -15, -2
  21. global dataset_pathname, dataset_title, pass_through_string
  22. global out_filename, png_filename
  23. # experimental
  24. telnet_workers = []
  25. ssh_workers = []
  26. nr_local_worker = 1
  27. # process command line options, set global parameters
  28. def process_options(argv=sys.argv):
  29. global fold
  30. global c_begin, c_end, c_step
  31. global g_begin, g_end, g_step
  32. global dataset_pathname, dataset_title, pass_through_string
  33. global svmtrain_exe, gnuplot_exe, gnuplot, out_filename, png_filename
  34. usage = """\
  35. Usage: grid.py [-log2c begin,end,step] [-log2g begin,end,step] [-v fold]
  36. [-svmtrain pathname] [-gnuplot pathname] [-out pathname] [-png pathname]
  37. [additional parameters for svm-train] dataset"""
  38. if len(argv) < 2:
  39. print usage
  40. sys.exit(1)
  41. dataset_pathname = argv[-1]
  42. dataset_title = os.path.split(dataset_pathname)[1]
  43. out_filename = '%s.out' % dataset_title
  44. png_filename = '%s.png' % dataset_title
  45. pass_through_options = []
  46. i = 1
  47. while i < len(argv) - 1:
  48. if argv[i] == "-log2c":
  49. i = i + 1
  50. (c_begin,c_end,c_step) = map(float,split(argv[i],","))
  51. elif argv[i] == "-log2g":
  52. i = i + 1
  53. (g_begin,g_end,g_step) = map(float,split(argv[i],","))
  54. elif argv[i] == "-v":
  55. i = i + 1
  56. fold = argv[i]
  57. elif argv[i] in ('-c','-g'):
  58. print "Option -c and -g are renamed."
  59. print usage
  60. sys.exit(1)
  61. elif argv[i] == '-svmtrain':
  62. i = i + 1
  63. svmtrain_exe = argv[i]
  64. elif argv[i] == '-gnuplot':
  65. i = i + 1
  66. gnuplot_exe = argv[i]
  67. elif argv[i] == '-out':
  68. i = i + 1
  69. out_filename = argv[i]
  70. elif argv[i] == '-png':
  71. i = i + 1
  72. png_filename = argv[i]
  73. else:
  74. pass_through_options.append(argv[i])
  75. i = i + 1
  76. pass_through_string = join(pass_through_options," ")
  77. assert os.path.exists(svmtrain_exe),"svm-train executable not found"
  78. assert os.path.exists(gnuplot_exe),"gnuplot executable not found"
  79. assert os.path.exists(dataset_pathname),"dataset not found"
  80. gnuplot = Popen(gnuplot_exe,stdin = PIPE).stdin
  81. def range_f(begin,end,step):
  82. # like range, but works on non-integer too
  83. seq = []
  84. while True:
  85. if step > 0 and begin > end: break
  86. if step < 0 and begin < end: break
  87. seq.append(begin)
  88. begin = begin + step
  89. return seq
  90. def permute_sequence(seq):
  91. n = len(seq)
  92. if n <= 1: return seq
  93. mid = int(n/2)
  94. left = permute_sequence(seq[:mid])
  95. right = permute_sequence(seq[mid+1:])
  96. ret = [seq[mid]]
  97. while left or right:
  98. if left: ret.append(left.pop(0))
  99. if right: ret.append(right.pop(0))
  100. return ret
  101. def redraw(db,best_param,tofile=False):
  102. if len(db) == 0: return
  103. begin_level = round(max(map(lambda(x):x[2],db))) - 3
  104. step_size = 0.5
  105. best_log2c,best_log2g,best_rate = best_param
  106. if tofile:
  107. gnuplot.write("set term png transparent small\n")
  108. gnuplot.write("set output \"%s\"\n" % png_filename.replace('\\','\\\\'))
  109. #gnuplot.write("set term postscript color solid\n")
  110. #gnuplot.write("set output \"%s.ps\"\n" % dataset_title)
  111. elif is_win32:
  112. gnuplot.write("set term windows\n")
  113. else:
  114. gnuplot.write("set term x11\n")
  115. gnuplot.write("set xlabel \"log2(C)\"\n")
  116. gnuplot.write("set ylabel \"log2(gamma)\"\n")
  117. gnuplot.write("set xrange [%s:%s]\n" % (c_begin,c_end))
  118. gnuplot.write("set yrange [%s:%s]\n" % (g_begin,g_end))
  119. gnuplot.write("set contour\n")
  120. gnuplot.write("set cntrparam levels incremental %s,%s,100\n" % (begin_level,step_size))
  121. gnuplot.write("unset surface\n")
  122. gnuplot.write("unset ztics\n")
  123. gnuplot.write("set view 0,0\n")
  124. gnuplot.write("set title \"%s\"\n" % dataset_title)
  125. gnuplot.write("unset label\n")
  126. gnuplot.write("set label \"Best log2(C) = %s log2(gamma) = %s accuracy = %s%%\" \
  127. at screen 0.5,0.85 center\n" % \
  128. (best_log2c, best_log2g, best_rate))
  129. gnuplot.write("set label \"C = %s gamma = %s\""
  130. " at screen 0.5,0.8 center\n" % (2**best_log2c, 2**best_log2g))
  131. gnuplot.write("splot \"-\" with lines\n")
  132. def cmp (x,y):
  133. if x[0] < y[0]: return -1
  134. if x[0] > y[0]: return 1
  135. if x[1] > y[1]: return -1
  136. if x[1] < y[1]: return 1
  137. return 0
  138. db.sort(cmp)
  139. prevc = db[0][0]
  140. for line in db:
  141. if prevc != line[0]:
  142. gnuplot.write("\n")
  143. prevc = line[0]
  144. gnuplot.write("%s %s %s\n" % line)
  145. gnuplot.write("e\n")
  146. gnuplot.write("\n") # force gnuplot back to prompt when term set failure
  147. gnuplot.flush()
  148. def calculate_jobs():
  149. c_seq = permute_sequence(range_f(c_begin,c_end,c_step))
  150. g_seq = permute_sequence(range_f(g_begin,g_end,g_step))
  151. nr_c = float(len(c_seq))
  152. nr_g = float(len(g_seq))
  153. i = 0
  154. j = 0
  155. jobs = []
  156. while i < nr_c or j < nr_g:
  157. if i/nr_c < j/nr_g:
  158. # increase C resolution
  159. line = []
  160. for k in range(0,j):
  161. line.append((c_seq[i],g_seq[k]))
  162. i = i + 1
  163. jobs.append(line)
  164. else:
  165. # increase g resolution
  166. line = []
  167. for k in range(0,i):
  168. line.append((c_seq[k],g_seq[j]))
  169. j = j + 1
  170. jobs.append(line)
  171. return jobs
  172. class WorkerStopToken: # used to notify the worker to stop
  173. pass
  174. class Worker(Thread):
  175. def __init__(self,name,job_queue,result_queue):
  176. Thread.__init__(self)
  177. self.name = name
  178. self.job_queue = job_queue
  179. self.result_queue = result_queue
  180. def run(self):
  181. while True:
  182. (cexp,gexp) = self.job_queue.get()
  183. if cexp is WorkerStopToken:
  184. self.job_queue.put((cexp,gexp))
  185. # print 'worker %s stop.' % self.name
  186. break
  187. try:
  188. rate = self.run_one(2.0**cexp,2.0**gexp)
  189. if rate is None: raise "get no rate"
  190. except:
  191. # we failed, let others do that and we just quit
  192. traceback.print_tb(sys.exc_traceback)
  193. self.job_queue.put((cexp,gexp))
  194. print 'worker %s quit.' % self.name
  195. break
  196. else:
  197. self.result_queue.put((self.name,cexp,gexp,rate))
  198. class LocalWorker(Worker):
  199. def run_one(self,c,g):
  200. cmdline = '%s -c %s -g %s -v %s %s %s' % \
  201. (svmtrain_exe,c,g,fold,pass_through_string,dataset_pathname)
  202. result = Popen(cmdline,shell=True,stdout=PIPE).stdout
  203. for line in result.readlines():
  204. if find(line,"Cross") != -1:
  205. return float(split(line)[-1][0:-1])
  206. class SSHWorker(Worker):
  207. def __init__(self,name,job_queue,result_queue,host):
  208. Worker.__init__(self,name,job_queue,result_queue)
  209. self.host = host
  210. self.cwd = os.getcwd()
  211. def run_one(self,c,g):
  212. cmdline = 'ssh -x %s "cd %s; %s -c %s -g %s -v %s %s %s"' % \
  213. (self.host,self.cwd,
  214. svmtrain_exe,c,g,fold,pass_through_string,dataset_pathname)
  215. result = Popen(cmdline,shell=True,stdout=PIPE).stdout
  216. for line in result.readlines():
  217. if find(line,"Cross") != -1:
  218. return float(split(line)[-1][0:-1])
  219. class TelnetWorker(Worker):
  220. def __init__(self,name,job_queue,result_queue,host,username,password):
  221. Worker.__init__(self,name,job_queue,result_queue)
  222. self.host = host
  223. self.username = username
  224. self.password = password
  225. def run(self):
  226. import telnetlib
  227. self.tn = tn = telnetlib.Telnet(self.host)
  228. tn.read_until("login: ")
  229. tn.write(self.username + "\n")
  230. tn.read_until("Password: ")
  231. tn.write(self.password + "\n")
  232. # XXX: how to know whether login is successful?
  233. tn.read_until(self.username)
  234. #
  235. print 'login ok', self.host
  236. tn.write("cd "+os.getcwd()+"\n")
  237. Worker.run(self)
  238. tn.write("exit\n")
  239. def run_one(self,c,g):
  240. cmdline = '%s -c %s -g %s -v %s %s %s' % \
  241. (svmtrain_exe,c,g,fold,pass_through_string,dataset_pathname)
  242. result = self.tn.write(cmdline+'\n')
  243. (idx,matchm,output) = self.tn.expect(['Cross.*\n'])
  244. for line in split(output,'\n'):
  245. if find(line,"Cross") != -1:
  246. return float(split(line)[-1][0:-1])
  247. def main():
  248. # set parameters
  249. process_options()
  250. # put jobs in queue
  251. jobs = calculate_jobs()
  252. job_queue = Queue.Queue(0)
  253. result_queue = Queue.Queue(0)
  254. for line in jobs:
  255. for (c,g) in line:
  256. job_queue.put((c,g))
  257. # hack the queue to become a stack --
  258. # this is important when some thread
  259. # failed and re-put a job. If we still
  260. # use FIFO, the job will be put
  261. # into the end of the queue, and the graph
  262. # will only be updated in the end
  263. def _put(self,item):
  264. if sys.hexversion >= 0x020400A1:
  265. self.queue.appendleft(item)
  266. else:
  267. self.queue.insert(0,item)
  268. import new
  269. job_queue._put = new.instancemethod(_put,job_queue,job_queue.__class__)
  270. # fire telnet workers
  271. if telnet_workers:
  272. nr_telnet_worker = len(telnet_workers)
  273. username = getpass.getuser()
  274. password = getpass.getpass()
  275. for host in telnet_workers:
  276. TelnetWorker(host,job_queue,result_queue,
  277. host,username,password).start()
  278. # fire ssh workers
  279. if ssh_workers:
  280. for host in ssh_workers:
  281. SSHWorker(host,job_queue,result_queue,host).start()
  282. # fire local workers
  283. for i in range(nr_local_worker):
  284. LocalWorker('local',job_queue,result_queue).start()
  285. # gather results
  286. done_jobs = {}
  287. result_file = open(out_filename,'w',0)
  288. db = []
  289. best_rate = -1
  290. best_c1,best_g1 = None,None
  291. for line in jobs:
  292. for (c,g) in line:
  293. while not done_jobs.has_key((c,g)):
  294. (worker,c1,g1,rate) = result_queue.get()
  295. done_jobs[(c1,g1)] = rate
  296. result_file.write('%s %s %s\n' %(c1,g1,rate))
  297. result_file.flush()
  298. print "[%s] %s %s %s" % (worker,c1,g1,rate),
  299. if (rate > best_rate) or (rate==best_rate and g1==best_g1 and c1<best_c1):
  300. best_rate = rate
  301. best_c1,best_g1=c1,g1
  302. best_c = 2.0**c1
  303. best_g = 2.0**g1
  304. print " (best c=%s, g=%s, rate=%s)" % \
  305. (best_c, best_g, best_rate)
  306. db.append((c,g,done_jobs[(c,g)]))
  307. redraw(db,[best_c1, best_g1, best_rate])
  308. redraw(db,[best_c1, best_g1, best_rate],True)
  309. job_queue.put((WorkerStopToken,None))
  310. print "%s %s %s" % (best_c, best_g, best_rate)
  311. main()