You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

run_weisfeilerLehmankernel_acyclic-checkpoint.ipynb 181 kB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015
  1. {
  2. "cells": [
  3. {
  4. "cell_type": "code",
  5. "execution_count": 1,
  6. "metadata": {},
  7. "outputs": [
  8. {
  9. "name": "stdout",
  10. "output_type": "stream",
  11. "text": [
  12. "\n",
  13. " --- This is a regression problem ---\n",
  14. "\n",
  15. "\n",
  16. " #--- calculating kernel matrix when height = 0.0 ---#\n",
  17. "\n",
  18. " Loading dataset from file...\n",
  19. "\n",
  20. " Calculating kernel matrix, this could take a while...\n",
  21. "\n",
  22. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 0.38979601860046387 seconds ---\n",
  23. "[[ 5. 6. 4. ..., 20. 20. 20.]\n",
  24. " [ 6. 8. 4. ..., 20. 20. 20.]\n",
  25. " [ 4. 4. 5. ..., 21. 21. 21.]\n",
  26. " ..., \n",
  27. " [ 20. 20. 21. ..., 101. 101. 101.]\n",
  28. " [ 20. 20. 21. ..., 101. 101. 101.]\n",
  29. " [ 20. 20. 21. ..., 101. 101. 101.]]\n",
  30. "\n",
  31. " Saving kernel matrix to file...\n",
  32. "\n",
  33. " Mean performance on train set: 17.681582\n",
  34. "With standard deviation: 0.713183\n",
  35. "\n",
  36. " Mean performance on test set: 15.685879\n",
  37. "With standard deviation: 4.139197\n",
  38. "\n",
  39. "\n",
  40. " #--- calculating kernel matrix when height = 1.0 ---#\n",
  41. "\n",
  42. " Loading dataset from file...\n",
  43. "\n",
  44. " Calculating kernel matrix, this could take a while...\n",
  45. "\n",
  46. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 0.8205692768096924 seconds ---\n",
  47. "[[ 10. 10. 4. ..., 20. 20. 20.]\n",
  48. " [ 10. 16. 4. ..., 20. 20. 20.]\n",
  49. " [ 4. 4. 10. ..., 22. 22. 24.]\n",
  50. " ..., \n",
  51. " [ 20. 20. 22. ..., 130. 130. 122.]\n",
  52. " [ 20. 20. 22. ..., 130. 130. 122.]\n",
  53. " [ 20. 20. 24. ..., 122. 122. 154.]]\n",
  54. "\n",
  55. " Saving kernel matrix to file...\n",
  56. "\n",
  57. " Mean performance on train set: 6.270014\n",
  58. "With standard deviation: 0.654734\n",
  59. "\n",
  60. " Mean performance on test set: 7.550458\n",
  61. "With standard deviation: 2.331786\n",
  62. "\n",
  63. "\n",
  64. " #--- calculating kernel matrix when height = 2.0 ---#\n",
  65. "\n",
  66. " Loading dataset from file...\n",
  67. "\n",
  68. " Calculating kernel matrix, this could take a while...\n",
  69. "\n",
  70. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 1.375309705734253 seconds ---\n",
  71. "[[ 15. 10. 4. ..., 20. 20. 20.]\n",
  72. " [ 10. 24. 4. ..., 20. 20. 20.]\n",
  73. " [ 4. 4. 15. ..., 22. 22. 26.]\n",
  74. " ..., \n",
  75. " [ 20. 20. 22. ..., 159. 151. 124.]\n",
  76. " [ 20. 20. 22. ..., 151. 153. 124.]\n",
  77. " [ 20. 20. 26. ..., 124. 124. 185.]]\n",
  78. "\n",
  79. " Saving kernel matrix to file...\n",
  80. "\n",
  81. " Mean performance on train set: 4.450682\n",
  82. "With standard deviation: 0.882129\n",
  83. "\n",
  84. " Mean performance on test set: 9.728466\n",
  85. "With standard deviation: 2.057669\n",
  86. "\n",
  87. "\n",
  88. " #--- calculating kernel matrix when height = 3.0 ---#\n",
  89. "\n",
  90. " Loading dataset from file...\n",
  91. "\n",
  92. " Calculating kernel matrix, this could take a while...\n",
  93. "\n",
  94. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 1.8636789321899414 seconds ---\n",
  95. "[[ 20. 10. 4. ..., 20. 20. 20.]\n",
  96. " [ 10. 32. 4. ..., 20. 20. 20.]\n",
  97. " [ 4. 4. 20. ..., 22. 22. 26.]\n",
  98. " ..., \n",
  99. " [ 20. 20. 22. ..., 188. 159. 124.]\n",
  100. " [ 20. 20. 22. ..., 159. 168. 124.]\n",
  101. " [ 20. 20. 26. ..., 124. 124. 202.]]\n",
  102. "\n",
  103. " Saving kernel matrix to file...\n",
  104. "\n",
  105. " Mean performance on train set: 2.270586\n",
  106. "With standard deviation: 0.481516\n",
  107. "\n",
  108. " Mean performance on test set: 11.296110\n",
  109. "With standard deviation: 2.799944\n",
  110. "\n",
  111. "\n",
  112. " #--- calculating kernel matrix when height = 4.0 ---#\n",
  113. "\n",
  114. " Loading dataset from file...\n",
  115. "\n",
  116. " Calculating kernel matrix, this could take a while...\n",
  117. "\n",
  118. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 2.5077457427978516 seconds ---\n",
  119. "[[ 25. 10. 4. ..., 20. 20. 20.]\n",
  120. " [ 10. 40. 4. ..., 20. 20. 20.]\n",
  121. " [ 4. 4. 25. ..., 22. 22. 26.]\n",
  122. " ..., \n",
  123. " [ 20. 20. 22. ..., 217. 159. 124.]\n",
  124. " [ 20. 20. 22. ..., 159. 183. 124.]\n",
  125. " [ 20. 20. 26. ..., 124. 124. 213.]]\n",
  126. "\n",
  127. " Saving kernel matrix to file...\n",
  128. "\n",
  129. " Mean performance on train set: 1.074035\n",
  130. "With standard deviation: 0.637823\n",
  131. "\n",
  132. " Mean performance on test set: 12.808303\n",
  133. "With standard deviation: 3.446939\n",
  134. "\n",
  135. "\n",
  136. " #--- calculating kernel matrix when height = 5.0 ---#\n",
  137. "\n",
  138. " Loading dataset from file...\n",
  139. "\n",
  140. " Calculating kernel matrix, this could take a while...\n",
  141. "\n",
  142. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 2.8235607147216797 seconds ---\n",
  143. "[[ 30. 10. 4. ..., 20. 20. 20.]\n",
  144. " [ 10. 48. 4. ..., 20. 20. 20.]\n",
  145. " [ 4. 4. 30. ..., 22. 22. 26.]\n",
  146. " ..., \n",
  147. " [ 20. 20. 22. ..., 246. 159. 124.]\n",
  148. " [ 20. 20. 22. ..., 159. 198. 124.]\n",
  149. " [ 20. 20. 26. ..., 124. 124. 224.]]\n",
  150. "\n",
  151. " Saving kernel matrix to file...\n",
  152. "\n",
  153. " Mean performance on train set: 0.700602\n",
  154. "With standard deviation: 0.572640\n",
  155. "\n",
  156. " Mean performance on test set: 14.017923\n",
  157. "With standard deviation: 3.675042\n",
  158. "\n",
  159. "\n",
  160. " #--- calculating kernel matrix when height = 6.0 ---#\n",
  161. "\n",
  162. " Loading dataset from file...\n",
  163. "\n",
  164. " Calculating kernel matrix, this could take a while...\n",
  165. "\n",
  166. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 3.458494186401367 seconds ---\n",
  167. "[[ 35. 10. 4. ..., 20. 20. 20.]\n",
  168. " [ 10. 56. 4. ..., 20. 20. 20.]\n",
  169. " [ 4. 4. 35. ..., 22. 22. 26.]\n",
  170. " ..., \n",
  171. " [ 20. 20. 22. ..., 275. 159. 124.]\n",
  172. " [ 20. 20. 22. ..., 159. 213. 124.]\n",
  173. " [ 20. 20. 26. ..., 124. 124. 235.]]\n",
  174. "\n",
  175. " Saving kernel matrix to file...\n",
  176. "\n",
  177. " Mean performance on train set: 0.691515\n",
  178. "With standard deviation: 0.564620\n",
  179. "\n",
  180. " Mean performance on test set: 14.918434\n",
  181. "With standard deviation: 3.805352\n",
  182. "\n",
  183. "\n",
  184. " #--- calculating kernel matrix when height = 7.0 ---#\n",
  185. "\n",
  186. " Loading dataset from file...\n",
  187. "\n",
  188. " Calculating kernel matrix, this could take a while...\n",
  189. "\n",
  190. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 3.861224889755249 seconds ---\n",
  191. "[[ 40. 10. 4. ..., 20. 20. 20.]\n",
  192. " [ 10. 64. 4. ..., 20. 20. 20.]\n",
  193. " [ 4. 4. 40. ..., 22. 22. 26.]\n",
  194. " ..., \n",
  195. " [ 20. 20. 22. ..., 304. 159. 124.]\n",
  196. " [ 20. 20. 22. ..., 159. 228. 124.]\n",
  197. " [ 20. 20. 26. ..., 124. 124. 246.]]\n",
  198. "\n",
  199. " Saving kernel matrix to file...\n",
  200. "\n",
  201. " Mean performance on train set: 0.691516\n",
  202. "With standard deviation: 0.564620\n",
  203. "\n",
  204. " Mean performance on test set: 15.629476\n",
  205. "With standard deviation: 3.865387\n",
  206. "\n",
  207. "\n",
  208. " #--- calculating kernel matrix when height = 8.0 ---#\n",
  209. "\n",
  210. " Loading dataset from file...\n",
  211. "\n",
  212. " Calculating kernel matrix, this could take a while...\n",
  213. "\n",
  214. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 4.295838117599487 seconds ---\n",
  215. "[[ 45. 10. 4. ..., 20. 20. 20.]\n",
  216. " [ 10. 72. 4. ..., 20. 20. 20.]\n",
  217. " [ 4. 4. 45. ..., 22. 22. 26.]\n",
  218. " ..., \n",
  219. " [ 20. 20. 22. ..., 333. 159. 124.]\n",
  220. " [ 20. 20. 22. ..., 159. 243. 124.]\n",
  221. " [ 20. 20. 26. ..., 124. 124. 257.]]\n",
  222. "\n",
  223. " Saving kernel matrix to file...\n",
  224. "\n",
  225. " Mean performance on train set: 0.691515\n",
  226. "With standard deviation: 0.564620\n",
  227. "\n",
  228. " Mean performance on test set: 16.214369\n",
  229. "With standard deviation: 3.928756\n",
  230. "\n",
  231. "\n",
  232. " #--- calculating kernel matrix when height = 9.0 ---#\n",
  233. "\n",
  234. " Loading dataset from file...\n",
  235. "\n",
  236. " Calculating kernel matrix, this could take a while...\n",
  237. "\n",
  238. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 5.008287668228149 seconds ---\n",
  239. "[[ 50. 10. 4. ..., 20. 20. 20.]\n",
  240. " [ 10. 80. 4. ..., 20. 20. 20.]\n",
  241. " [ 4. 4. 50. ..., 22. 22. 26.]\n",
  242. " ..., \n",
  243. " [ 20. 20. 22. ..., 362. 159. 124.]\n",
  244. " [ 20. 20. 22. ..., 159. 258. 124.]\n",
  245. " [ 20. 20. 26. ..., 124. 124. 268.]]\n",
  246. "\n",
  247. " Saving kernel matrix to file...\n",
  248. "\n",
  249. " Mean performance on train set: 0.691515\n",
  250. "With standard deviation: 0.564620\n",
  251. "\n",
  252. " Mean performance on test set: 16.725744\n",
  253. "With standard deviation: 3.993095\n",
  254. "\n",
  255. "\n",
  256. " #--- calculating kernel matrix when height = 10.0 ---#\n",
  257. "\n",
  258. " Loading dataset from file...\n",
  259. "\n",
  260. " Calculating kernel matrix, this could take a while...\n",
  261. "\n",
  262. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 5.347799301147461 seconds ---\n",
  263. "[[ 55. 10. 4. ..., 20. 20. 20.]\n",
  264. " [ 10. 88. 4. ..., 20. 20. 20.]\n",
  265. " [ 4. 4. 55. ..., 22. 22. 26.]\n",
  266. " ..., \n",
  267. " [ 20. 20. 22. ..., 391. 159. 124.]\n",
  268. " [ 20. 20. 22. ..., 159. 273. 124.]\n",
  269. " [ 20. 20. 26. ..., 124. 124. 279.]]\n",
  270. "\n",
  271. " Saving kernel matrix to file...\n",
  272. "\n",
  273. " Mean performance on train set: 0.691516\n",
  274. "With standard deviation: 0.564621\n",
  275. "\n",
  276. " Mean performance on test set: 17.186401\n",
  277. "With standard deviation: 4.056724\n",
  278. "\n",
  279. "\n",
  280. " height RMSE_test std_test RMSE_train std_train k_time\n",
  281. "-------- ----------- ---------- ------------ ----------- --------\n",
  282. " 0 15.6859 4.1392 17.6816 0.713183 0.389796\n",
  283. " 1 7.55046 2.33179 6.27001 0.654734 0.820569\n",
  284. " 2 9.72847 2.05767 4.45068 0.882129 1.37531\n",
  285. " 3 11.2961 2.79994 2.27059 0.481516 1.86368\n",
  286. " 4 12.8083 3.44694 1.07403 0.637823 2.50775\n",
  287. " 5 14.0179 3.67504 0.700602 0.57264 2.82356\n",
  288. " 6 14.9184 3.80535 0.691515 0.56462 3.45849\n",
  289. " 7 15.6295 3.86539 0.691516 0.56462 3.86122\n",
  290. " 8 16.2144 3.92876 0.691515 0.56462 4.29584\n",
  291. " 9 16.7257 3.9931 0.691515 0.56462 5.00829\n",
  292. " 10 17.1864 4.05672 0.691516 0.564621 5.3478\n"
  293. ]
  294. }
  295. ],
  296. "source": [
  297. "# wl subtree kernel\n",
  298. "%load_ext line_profiler\n",
  299. "\n",
  300. "import numpy as np\n",
  301. "import sys\n",
  302. "sys.path.insert(0, \"../\")\n",
  303. "from pygraph.utils.utils import kernel_train_test\n",
  304. "from pygraph.kernels.weisfeilerLehmanKernel import weisfeilerlehmankernel, _wl_subtreekernel_do\n",
  305. "\n",
  306. "datafile = '../../../../datasets/acyclic/Acyclic/dataset_bps.ds'\n",
  307. "kernel_file_path = 'kernelmatrices_weisfeilerlehman_subtree_acyclic/'\n",
  308. "\n",
  309. "kernel_para = dict(node_label = 'atom', edge_label = 'bond_type')\n",
  310. "\n",
  311. "kernel_train_test(datafile, kernel_file_path, weisfeilerlehmankernel, kernel_para, \\\n",
  312. " hyper_name = 'height', hyper_range = np.linspace(0, 10, 11), normalize = False)\n",
  313. "\n",
  314. "# %lprun -f _wl_subtreekernel_do \\\n",
  315. "# kernel_train_test(datafile, kernel_file_path, weisfeilerlehmankernel, kernel_para, \\\n",
  316. "# hyper_name = 'height', hyper_range = np.linspace(0, 10, 11), normalize = False)"
  317. ]
  318. },
  319. {
  320. "cell_type": "code",
  321. "execution_count": 1,
  322. "metadata": {},
  323. "outputs": [
  324. {
  325. "ename": "ImportError",
  326. "evalue": "cannot import name 'NUMPY_MKL'",
  327. "output_type": "error",
  328. "traceback": [
  329. "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
  330. "\u001b[1;31mImportError\u001b[0m Traceback (most recent call last)",
  331. "\u001b[1;32m<ipython-input-1-e7b9d5ef03e3>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 5\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0msys\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 6\u001b[0m \u001b[0msys\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mpath\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0minsert\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m0\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m\"../\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 7\u001b[1;33m \u001b[1;32mfrom\u001b[0m \u001b[0mpygraph\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mutils\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mutils\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mkernel_train_test\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 8\u001b[0m \u001b[1;32mfrom\u001b[0m \u001b[0mpygraph\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mkernels\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mweisfeilerLehmanKernel\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mweisfeilerlehmankernel\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0m_wl_subtreekernel_do\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 9\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
  332. "\u001b[1;32mE:\\课程及课件\\Doctorant\\py-graph\\pygraph\\utils\\utils.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 183\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 184\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mrandom\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 185\u001b[1;33m \u001b[1;32mfrom\u001b[0m \u001b[0msklearn\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mkernel_ridge\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mKernelRidge\u001b[0m \u001b[1;31m# 0.17\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 186\u001b[0m \u001b[1;32mfrom\u001b[0m \u001b[0msklearn\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mmetrics\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0maccuracy_score\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mmean_squared_error\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 187\u001b[0m \u001b[1;32mfrom\u001b[0m \u001b[0msklearn\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0msvm\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
  333. "\u001b[1;32md:\\python\\python36\\lib\\site-packages\\sklearn\\__init__.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 132\u001b[0m \u001b[1;32melse\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 133\u001b[0m \u001b[1;32mfrom\u001b[0m \u001b[1;33m.\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0m__check_build\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m--> 134\u001b[1;33m \u001b[1;32mfrom\u001b[0m \u001b[1;33m.\u001b[0m\u001b[0mbase\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mclone\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 135\u001b[0m \u001b[0m__check_build\u001b[0m \u001b[1;31m# avoid flakes unused variable error\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 136\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
  334. "\u001b[1;32md:\\python\\python36\\lib\\site-packages\\sklearn\\base.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 9\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 10\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mnumpy\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0mnp\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 11\u001b[1;33m \u001b[1;32mfrom\u001b[0m \u001b[0mscipy\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0msparse\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 12\u001b[0m \u001b[1;32mfrom\u001b[0m \u001b[1;33m.\u001b[0m\u001b[0mexternals\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0msix\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 13\u001b[0m \u001b[1;32mfrom\u001b[0m \u001b[1;33m.\u001b[0m\u001b[0mutils\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mfixes\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0msignature\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
  335. "\u001b[1;32md:\\python\\python36\\lib\\site-packages\\scipy\\__init__.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m 59\u001b[0m \u001b[0m__all__\u001b[0m \u001b[1;33m=\u001b[0m \u001b[1;33m[\u001b[0m\u001b[1;34m'test'\u001b[0m\u001b[1;33m]\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 60\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m---> 61\u001b[1;33m \u001b[1;32mfrom\u001b[0m \u001b[0mnumpy\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0m_distributor_init\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mNUMPY_MKL\u001b[0m \u001b[1;31m# requires numpy+mkl\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m 62\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 63\u001b[0m \u001b[1;32mfrom\u001b[0m \u001b[0mnumpy\u001b[0m \u001b[1;32mimport\u001b[0m \u001b[0mshow_config\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0mshow_numpy_config\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
  336. "\u001b[1;31mImportError\u001b[0m: cannot import name 'NUMPY_MKL'"
  337. ]
  338. }
  339. ],
  340. "source": [
  341. "# WL sp kernel\n",
  342. "%load_ext line_profiler\n",
  343. "\n",
  344. "import numpy as np\n",
  345. "import sys\n",
  346. "sys.path.insert(0, \"../\")\n",
  347. "from pygraph.utils.utils import kernel_train_test\n",
  348. "from pygraph.kernels.weisfeilerLehmanKernel import weisfeilerlehmankernel, _wl_subtreekernel_do\n",
  349. "\n",
  350. "datafile = '../../../../datasets/acyclic/Acyclic/dataset_bps.ds'\n",
  351. "kernel_file_path = 'kernelmatrices_weisfeilerlehman_subtree_acyclic/'\n",
  352. "\n",
  353. "kernel_para = dict(node_label = 'atom', edge_label = 'bond_type', base_kernel = 'sp')\n",
  354. "\n",
  355. "kernel_train_test(datafile, kernel_file_path, weisfeilerlehmankernel, kernel_para, \\\n",
  356. " hyper_name = 'height', hyper_range = np.linspace(0, 10, 11), normalize = False)\n",
  357. "\n",
  358. "# %lprun -f _wl_subtreekernel_do \\\n",
  359. "# kernel_train_test(datafile, kernel_file_path, weisfeilerlehmankernel, kernel_para, \\\n",
  360. "# hyper_name = 'height', hyper_range = np.linspace(0, 10, 11), normalize = False)"
  361. ]
  362. },
  363. {
  364. "cell_type": "code",
  365. "execution_count": null,
  366. "metadata": {},
  367. "outputs": [],
  368. "source": [
  369. "# results\n",
  370. "\n",
  371. "# with y normalization\n",
  372. " height RMSE_test std_test RMSE_train std_train k_time\n",
  373. "-------- ----------- ---------- ------------ ----------- --------\n",
  374. " 0 36.2108 7.33179 38.6059 1.57064 0.379475\n",
  375. " 1 9.00098 6.37145 6.76379 1.96568 0.844898\n",
  376. " 2 19.8113 4.04911 5.28757 1.81899 1.35308\n",
  377. " 3 25.0455 4.94276 2.3274 0.805733 1.81136\n",
  378. " 4 28.2255 6.5212 0.85156 0.423465 2.23098\n",
  379. " 5 30.6354 6.73647 3.35947 8.17561 2.71575\n",
  380. " 6 32.1027 6.85601 3.54105 8.71922 3.11459\n",
  381. " 7 32.9709 6.89606 6.94372 9.94045 3.55571\n",
  382. " 8 33.5112 6.90753 6.97339 9.76975 3.79657\n",
  383. " 9 33.8502 6.91427 11.8345 11.6213 4.41555\n",
  384. " 10 34.0963 6.93115 11.4257 11.2624 4.94888\n",
  385. "\n",
  386. "# without y normalization\n",
  387. " height RMSE_test std_test RMSE_train std_train k_time\n",
  388. "-------- ----------- ---------- ------------ ----------- --------\n",
  389. " 0 15.6859 4.1392 17.6816 0.713183 0.360443\n",
  390. " 1 7.55046 2.33179 6.27001 0.654734 0.837389\n",
  391. " 2 9.72847 2.05767 4.45068 0.882129 1.25317\n",
  392. " 3 11.2961 2.79994 2.27059 0.481516 1.79971\n",
  393. " 4 12.8083 3.44694 1.07403 0.637823 2.35346\n",
  394. " 5 14.0179 3.67504 0.700602 0.57264 2.78285\n",
  395. " 6 14.9184 3.80535 0.691515 0.56462 3.20764\n",
  396. " 7 15.6295 3.86539 0.691516 0.56462 3.71648\n",
  397. " 8 16.2144 3.92876 0.691515 0.56462 3.99213\n",
  398. " 9 16.7257 3.9931 0.691515 0.56462 4.26315\n",
  399. " 10 17.1864 4.05672 0.691516 0.564621 5.00918"
  400. ]
  401. },
  402. {
  403. "cell_type": "code",
  404. "execution_count": 3,
  405. "metadata": {
  406. "scrolled": true
  407. },
  408. "outputs": [
  409. {
  410. "name": "stdout",
  411. "output_type": "stream",
  412. "text": [
  413. "\n",
  414. "- This script take as input a kernel matrix\n",
  415. "and returns the classification or regression performance\n",
  416. "- The kernel matrix can be calculated using any of the graph kernels approaches\n",
  417. "- The criteria used for prediction are SVM for classification and kernel Ridge regression for regression\n",
  418. "- For predition we divide the data in training, validation and test. For each split, we first train on the train data, \n",
  419. "then evaluate the performance on the validation. We choose the optimal parameters for the validation set and finally\n",
  420. "provide the corresponding performance on the test set. If more than one split is performed, the final results \n",
  421. "correspond to the average of the performances on the test sets. \n",
  422. "\n",
  423. "@references\n",
  424. " Elisabetta Ghisu, https://github.com/eghisu/GraphKernels/blob/master/GraphKernelsCollection/python_scripts/compute_perf_gk.py\n",
  425. "\n",
  426. "\n",
  427. "\n",
  428. " #--- calculating kernel matrix when subtree height = 0 ---#\n",
  429. "\n",
  430. " Loading dataset from file...\n",
  431. "\n",
  432. " --- This is a regression problem ---\n",
  433. "\n",
  434. " Calculating kernel matrix, this could take a while...\n",
  435. "\n",
  436. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 0.3920705318450928 seconds ---\n",
  437. "[[ 5. 6. 4. ..., 20. 20. 20.]\n",
  438. " [ 6. 8. 4. ..., 20. 20. 20.]\n",
  439. " [ 4. 4. 5. ..., 21. 21. 21.]\n",
  440. " ..., \n",
  441. " [ 20. 20. 21. ..., 101. 101. 101.]\n",
  442. " [ 20. 20. 21. ..., 101. 101. 101.]\n",
  443. " [ 20. 20. 21. ..., 101. 101. 101.]]\n",
  444. "\n",
  445. " Saving kernel matrix to file...\n",
  446. "\n",
  447. " Mean performance on train set: 17.681582\n",
  448. "With standard deviation: 0.713183\n",
  449. "\n",
  450. " Mean performance on test set: 15.685879\n",
  451. "With standard deviation: 4.139197\n",
  452. "\n",
  453. "\n",
  454. " #--- calculating kernel matrix when subtree height = 1 ---#\n",
  455. "\n",
  456. " Loading dataset from file...\n",
  457. "\n",
  458. " --- This is a regression problem ---\n",
  459. "\n",
  460. " Calculating kernel matrix, this could take a while...\n",
  461. "\n",
  462. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 0.8578901290893555 seconds ---\n",
  463. "[[ 10. 10. 4. ..., 20. 20. 20.]\n",
  464. " [ 10. 16. 4. ..., 20. 20. 20.]\n",
  465. " [ 4. 4. 10. ..., 22. 22. 24.]\n",
  466. " ..., \n",
  467. " [ 20. 20. 22. ..., 130. 130. 122.]\n",
  468. " [ 20. 20. 22. ..., 130. 130. 122.]\n",
  469. " [ 20. 20. 24. ..., 122. 122. 154.]]\n",
  470. "\n",
  471. " Saving kernel matrix to file...\n",
  472. "\n",
  473. " Mean performance on train set: 6.270014\n",
  474. "With standard deviation: 0.654734\n",
  475. "\n",
  476. " Mean performance on test set: 7.550458\n",
  477. "With standard deviation: 2.331786\n",
  478. "\n",
  479. "\n",
  480. " #--- calculating kernel matrix when subtree height = 2 ---#\n",
  481. "\n",
  482. " Loading dataset from file...\n",
  483. "\n",
  484. " --- This is a regression problem ---\n",
  485. "\n",
  486. " Calculating kernel matrix, this could take a while...\n",
  487. "\n",
  488. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 1.264050006866455 seconds ---\n",
  489. "[[ 15. 10. 4. ..., 20. 20. 20.]\n",
  490. " [ 10. 24. 4. ..., 20. 20. 20.]\n",
  491. " [ 4. 4. 15. ..., 22. 22. 26.]\n",
  492. " ..., \n",
  493. " [ 20. 20. 22. ..., 159. 151. 124.]\n",
  494. " [ 20. 20. 22. ..., 151. 153. 124.]\n",
  495. " [ 20. 20. 26. ..., 124. 124. 185.]]\n",
  496. "\n",
  497. " Saving kernel matrix to file...\n",
  498. "\n",
  499. " Mean performance on train set: 4.450682\n",
  500. "With standard deviation: 0.882129\n",
  501. "\n",
  502. " Mean performance on test set: 9.728466\n",
  503. "With standard deviation: 2.057669\n",
  504. "\n",
  505. "\n",
  506. " #--- calculating kernel matrix when subtree height = 3 ---#\n",
  507. "\n",
  508. " Loading dataset from file...\n",
  509. "\n",
  510. " --- This is a regression problem ---\n",
  511. "\n",
  512. " Calculating kernel matrix, this could take a while...\n",
  513. "\n",
  514. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 1.731236219406128 seconds ---\n",
  515. "[[ 20. 10. 4. ..., 20. 20. 20.]\n",
  516. " [ 10. 32. 4. ..., 20. 20. 20.]\n",
  517. " [ 4. 4. 20. ..., 22. 22. 26.]\n",
  518. " ..., \n",
  519. " [ 20. 20. 22. ..., 188. 159. 124.]\n",
  520. " [ 20. 20. 22. ..., 159. 168. 124.]\n",
  521. " [ 20. 20. 26. ..., 124. 124. 202.]]\n",
  522. "\n",
  523. " Saving kernel matrix to file...\n",
  524. "\n",
  525. " Mean performance on train set: 2.270586\n",
  526. "With standard deviation: 0.481516\n",
  527. "\n",
  528. " Mean performance on test set: 11.296110\n",
  529. "With standard deviation: 2.799944\n",
  530. "\n",
  531. "\n",
  532. " #--- calculating kernel matrix when subtree height = 4 ---#\n",
  533. "\n",
  534. " Loading dataset from file...\n",
  535. "\n",
  536. " --- This is a regression problem ---\n",
  537. "\n",
  538. " Calculating kernel matrix, this could take a while...\n",
  539. "\n",
  540. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 2.1112847328186035 seconds ---\n",
  541. "[[ 25. 10. 4. ..., 20. 20. 20.]\n",
  542. " [ 10. 40. 4. ..., 20. 20. 20.]\n",
  543. " [ 4. 4. 25. ..., 22. 22. 26.]\n",
  544. " ..., \n",
  545. " [ 20. 20. 22. ..., 217. 159. 124.]\n",
  546. " [ 20. 20. 22. ..., 159. 183. 124.]\n",
  547. " [ 20. 20. 26. ..., 124. 124. 213.]]\n",
  548. "\n",
  549. " Saving kernel matrix to file...\n",
  550. "\n",
  551. " Mean performance on train set: 1.074035\n",
  552. "With standard deviation: 0.637823\n",
  553. "\n",
  554. " Mean performance on test set: 12.808303\n",
  555. "With standard deviation: 3.446939\n",
  556. "\n",
  557. "\n",
  558. " #--- calculating kernel matrix when subtree height = 5 ---#\n",
  559. "\n",
  560. " Loading dataset from file...\n",
  561. "\n",
  562. " --- This is a regression problem ---\n",
  563. "\n",
  564. " Calculating kernel matrix, this could take a while...\n",
  565. "\n",
  566. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 2.4751319885253906 seconds ---\n",
  567. "[[ 30. 10. 4. ..., 20. 20. 20.]\n",
  568. " [ 10. 48. 4. ..., 20. 20. 20.]\n",
  569. " [ 4. 4. 30. ..., 22. 22. 26.]\n",
  570. " ..., \n",
  571. " [ 20. 20. 22. ..., 246. 159. 124.]\n",
  572. " [ 20. 20. 22. ..., 159. 198. 124.]\n",
  573. " [ 20. 20. 26. ..., 124. 124. 224.]]\n",
  574. "\n",
  575. " Saving kernel matrix to file...\n",
  576. "\n",
  577. " Mean performance on train set: 0.700602\n",
  578. "With standard deviation: 0.572640\n",
  579. "\n",
  580. " Mean performance on test set: 14.017923\n",
  581. "With standard deviation: 3.675042\n",
  582. "\n",
  583. "\n",
  584. " #--- calculating kernel matrix when subtree height = 6 ---#\n",
  585. "\n",
  586. " Loading dataset from file...\n",
  587. "\n",
  588. " --- This is a regression problem ---\n",
  589. "\n",
  590. " Calculating kernel matrix, this could take a while...\n",
  591. "\n",
  592. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 2.8712213039398193 seconds ---\n",
  593. "[[ 35. 10. 4. ..., 20. 20. 20.]\n",
  594. " [ 10. 56. 4. ..., 20. 20. 20.]\n",
  595. " [ 4. 4. 35. ..., 22. 22. 26.]\n",
  596. " ..., \n",
  597. " [ 20. 20. 22. ..., 275. 159. 124.]\n",
  598. " [ 20. 20. 22. ..., 159. 213. 124.]\n",
  599. " [ 20. 20. 26. ..., 124. 124. 235.]]\n",
  600. "\n",
  601. " Saving kernel matrix to file...\n",
  602. "\n",
  603. " Mean performance on train set: 0.691515\n",
  604. "With standard deviation: 0.564620\n",
  605. "\n",
  606. " Mean performance on test set: 14.918434\n",
  607. "With standard deviation: 3.805352\n",
  608. "\n",
  609. "\n",
  610. " #--- calculating kernel matrix when subtree height = 7 ---#\n",
  611. "\n",
  612. " Loading dataset from file...\n",
  613. "\n",
  614. " --- This is a regression problem ---\n",
  615. "\n",
  616. " Calculating kernel matrix, this could take a while...\n",
  617. "\n",
  618. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 3.554422378540039 seconds ---\n",
  619. "[[ 40. 10. 4. ..., 20. 20. 20.]\n",
  620. " [ 10. 64. 4. ..., 20. 20. 20.]\n",
  621. " [ 4. 4. 40. ..., 22. 22. 26.]\n",
  622. " ..., \n",
  623. " [ 20. 20. 22. ..., 304. 159. 124.]\n",
  624. " [ 20. 20. 22. ..., 159. 228. 124.]\n",
  625. " [ 20. 20. 26. ..., 124. 124. 246.]]\n",
  626. "\n",
  627. " Saving kernel matrix to file...\n",
  628. "\n",
  629. " Mean performance on train set: 0.691516\n",
  630. "With standard deviation: 0.564620\n",
  631. "\n",
  632. " Mean performance on test set: 15.629476\n",
  633. "With standard deviation: 3.865387\n",
  634. "\n",
  635. "\n",
  636. " #--- calculating kernel matrix when subtree height = 8 ---#\n",
  637. "\n",
  638. " Loading dataset from file...\n",
  639. "\n",
  640. " --- This is a regression problem ---\n",
  641. "\n",
  642. " Calculating kernel matrix, this could take a while...\n",
  643. "\n",
  644. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 3.8757314682006836 seconds ---\n",
  645. "[[ 45. 10. 4. ..., 20. 20. 20.]\n",
  646. " [ 10. 72. 4. ..., 20. 20. 20.]\n",
  647. " [ 4. 4. 45. ..., 22. 22. 26.]\n",
  648. " ..., \n",
  649. " [ 20. 20. 22. ..., 333. 159. 124.]\n",
  650. " [ 20. 20. 22. ..., 159. 243. 124.]\n",
  651. " [ 20. 20. 26. ..., 124. 124. 257.]]\n",
  652. "\n",
  653. " Saving kernel matrix to file...\n",
  654. "\n",
  655. " Mean performance on train set: 0.691515\n",
  656. "With standard deviation: 0.564620\n",
  657. "\n",
  658. " Mean performance on test set: 16.214369\n",
  659. "With standard deviation: 3.928756\n",
  660. "\n",
  661. "\n",
  662. " #--- calculating kernel matrix when subtree height = 9 ---#\n",
  663. "\n",
  664. " Loading dataset from file...\n",
  665. "\n",
  666. " --- This is a regression problem ---\n",
  667. "\n",
  668. " Calculating kernel matrix, this could take a while...\n",
  669. "\n",
  670. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 4.205373764038086 seconds ---\n",
  671. "[[ 50. 10. 4. ..., 20. 20. 20.]\n",
  672. " [ 10. 80. 4. ..., 20. 20. 20.]\n",
  673. " [ 4. 4. 50. ..., 22. 22. 26.]\n",
  674. " ..., \n",
  675. " [ 20. 20. 22. ..., 362. 159. 124.]\n",
  676. " [ 20. 20. 22. ..., 159. 258. 124.]\n",
  677. " [ 20. 20. 26. ..., 124. 124. 268.]]\n",
  678. "\n",
  679. " Saving kernel matrix to file...\n",
  680. "\n",
  681. " Mean performance on train set: 0.691515\n",
  682. "With standard deviation: 0.564620\n",
  683. "\n",
  684. " Mean performance on test set: 16.725744\n",
  685. "With standard deviation: 3.993095\n",
  686. "\n",
  687. "\n",
  688. " #--- calculating kernel matrix when subtree height = 10 ---#\n",
  689. "\n",
  690. " Loading dataset from file...\n",
  691. "\n",
  692. " --- This is a regression problem ---\n",
  693. "\n",
  694. " Calculating kernel matrix, this could take a while...\n"
  695. ]
  696. },
  697. {
  698. "name": "stdout",
  699. "output_type": "stream",
  700. "text": [
  701. "\n",
  702. " --- Weisfeiler-Lehman subtree kernel matrix of size 185 built in 4.737298250198364 seconds ---\n",
  703. "[[ 55. 10. 4. ..., 20. 20. 20.]\n",
  704. " [ 10. 88. 4. ..., 20. 20. 20.]\n",
  705. " [ 4. 4. 55. ..., 22. 22. 26.]\n",
  706. " ..., \n",
  707. " [ 20. 20. 22. ..., 391. 159. 124.]\n",
  708. " [ 20. 20. 22. ..., 159. 273. 124.]\n",
  709. " [ 20. 20. 26. ..., 124. 124. 279.]]\n",
  710. "\n",
  711. " Saving kernel matrix to file...\n",
  712. "\n",
  713. " Mean performance on train set: 0.691516\n",
  714. "With standard deviation: 0.564621\n",
  715. "\n",
  716. " Mean performance on test set: 17.186401\n",
  717. "With standard deviation: 4.056724\n",
  718. "\n",
  719. "\n",
  720. " height RMSE_test std_test RMSE_train std_train k_time\n",
  721. "-------- ----------- ---------- ------------ ----------- --------\n",
  722. " 0 15.6859 4.1392 17.6816 0.713183 0.392071\n",
  723. " 1 7.55046 2.33179 6.27001 0.654734 0.85789\n",
  724. " 2 9.72847 2.05767 4.45068 0.882129 1.26405\n",
  725. " 3 11.2961 2.79994 2.27059 0.481516 1.73124\n",
  726. " 4 12.8083 3.44694 1.07403 0.637823 2.11128\n",
  727. " 5 14.0179 3.67504 0.700602 0.57264 2.47513\n",
  728. " 6 14.9184 3.80535 0.691515 0.56462 2.87122\n",
  729. " 7 15.6295 3.86539 0.691516 0.56462 3.55442\n",
  730. " 8 16.2144 3.92876 0.691515 0.56462 3.87573\n",
  731. " 9 16.7257 3.9931 0.691515 0.56462 4.20537\n",
  732. " 10 17.1864 4.05672 0.691516 0.564621 4.7373\n"
  733. ]
  734. }
  735. ],
  736. "source": [
  737. "# test of WL subtree kernel\n",
  738. "\n",
  739. "\"\"\"\n",
  740. "- This script take as input a kernel matrix\n",
  741. "and returns the classification or regression performance\n",
  742. "- The kernel matrix can be calculated using any of the graph kernels approaches\n",
  743. "- The criteria used for prediction are SVM for classification and kernel Ridge regression for regression\n",
  744. "- For predition we divide the data in training, validation and test. For each split, we first train on the train data, \n",
  745. "then evaluate the performance on the validation. We choose the optimal parameters for the validation set and finally\n",
  746. "provide the corresponding performance on the test set. If more than one split is performed, the final results \n",
  747. "correspond to the average of the performances on the test sets. \n",
  748. "\n",
  749. "@references\n",
  750. " Elisabetta Ghisu, https://github.com/eghisu/GraphKernels/blob/master/GraphKernelsCollection/python_scripts/compute_perf_gk.py\n",
  751. "\"\"\"\n",
  752. "\n",
  753. "print(__doc__)\n",
  754. "\n",
  755. "import sys\n",
  756. "import os\n",
  757. "import pathlib\n",
  758. "from collections import OrderedDict\n",
  759. "sys.path.insert(0, \"../\")\n",
  760. "from tabulate import tabulate\n",
  761. "\n",
  762. "import numpy as np\n",
  763. "import matplotlib.pyplot as plt\n",
  764. "\n",
  765. "from pygraph.kernels.weisfeilerLehmanKernel import weisfeilerlehmankernel\n",
  766. "from pygraph.utils.graphfiles import loadDataset\n",
  767. "from pygraph.utils.utils import split_train_test\n",
  768. "\n",
  769. "train_means_list = []\n",
  770. "train_stds_list = []\n",
  771. "test_means_list = []\n",
  772. "test_stds_list = []\n",
  773. "kernel_time_list = []\n",
  774. "\n",
  775. "for height in np.linspace(0, 10, 11):\n",
  776. " print('\\n\\n #--- calculating kernel matrix when subtree height = %d ---#' % height)\n",
  777. "\n",
  778. " print('\\n Loading dataset from file...')\n",
  779. " dataset, y = loadDataset(\"../../../../datasets/acyclic/Acyclic/dataset_bps.ds\")\n",
  780. " y = np.array(y)\n",
  781. "# print(y)\n",
  782. "\n",
  783. " # setup the parameters\n",
  784. " model_type = 'regression' # Regression or classification problem\n",
  785. " print('\\n --- This is a %s problem ---' % model_type)\n",
  786. "\n",
  787. "# datasize = len(dataset)\n",
  788. " trials = 100 # Trials for hyperparameters random search\n",
  789. " splits = 10 # Number of splits of the data\n",
  790. " alpha_grid = np.logspace(-10, 10, num = trials, base = 10) # corresponds to (2*C)^-1 in other linear models such as LogisticRegression\n",
  791. " C_grid = np.logspace(-10, 10, num = trials, base = 10)\n",
  792. "\n",
  793. "\n",
  794. " # set the output path\n",
  795. " kernel_file_path = 'kernelmatrices_weisfeilerlehman_subtree_acyclic/'\n",
  796. " if not os.path.exists(kernel_file_path):\n",
  797. " os.makedirs(kernel_file_path)\n",
  798. "\n",
  799. " \"\"\"\n",
  800. " - Here starts the main program\n",
  801. " - First we permute the data, then for each split we evaluate corresponding performances\n",
  802. " - In the end, the performances are averaged over the test sets\n",
  803. " \"\"\"\n",
  804. "\n",
  805. " # save kernel matrices to files / read kernel matrices from files\n",
  806. " kernel_file = kernel_file_path + 'km.ds'\n",
  807. " path = pathlib.Path(kernel_file)\n",
  808. " # get train set kernel matrix\n",
  809. " if path.is_file():\n",
  810. " print('\\n Loading the kernel matrix from file...')\n",
  811. " Kmatrix = np.loadtxt(kernel_file)# results\n",
  812. " print(Kmatrix)\n",
  813. " else:\n",
  814. " print('\\n Calculating kernel matrix, this could take a while...')\n",
  815. " Kmatrix, run_time = weisfeilerlehmankernel(dataset, node_label = 'atom', height = int(height))\n",
  816. " kernel_time_list.append(run_time)\n",
  817. " print(Kmatrix)\n",
  818. " print('\\n Saving kernel matrix to file...')\n",
  819. " # np.savetxt(kernel_file, Kmatrix)\n",
  820. "\n",
  821. " train_mean, train_std, test_mean, test_std = \\\n",
  822. " split_train_test(Kmatrix, y, alpha_grid, C_grid, splits, trials, model_type, normalize = False)\n",
  823. " \n",
  824. " train_means_list.append(train_mean)\n",
  825. " train_stds_list.append(train_std)\n",
  826. " test_means_list.append(test_mean)\n",
  827. " test_stds_list.append(test_std)\n",
  828. " \n",
  829. "print('\\n') \n",
  830. "table_dict = {'height': np.linspace(0, 10, 11), 'RMSE_test': test_means_list, 'std_test': test_stds_list, \\\n",
  831. " 'RMSE_train': train_means_list, 'std_train': train_stds_list, 'k_time': kernel_time_list}\n",
  832. "keyorder = ['height', 'RMSE_test', 'std_test', 'RMSE_train', 'std_train', 'k_time']\n",
  833. "print(tabulate(OrderedDict(sorted(table_dict.items(), key = lambda i:keyorder.index(i[0]))), headers='keys'))"
  834. ]
  835. },
  836. {
  837. "cell_type": "code",
  838. "execution_count": 8,
  839. "metadata": {
  840. "scrolled": true
  841. },
  842. "outputs": [
  843. {
  844. "name": "stdout",
  845. "output_type": "stream",
  846. "text": [
  847. "{'O', 'C'}\n",
  848. "{'O', 'C'}\n",
  849. "--- shortest path kernel built in 0.0002582073211669922 seconds ---\n",
  850. "3\n"
  851. ]
  852. },
  853. {
  854. "data": {
  855. "image/png": "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\n",
  856. "text/plain": [
  857. "<matplotlib.figure.Figure at 0x7f773eab40b8>"
  858. ]
  859. },
  860. "metadata": {},
  861. "output_type": "display_data"
  862. },
  863. {
  864. "name": "stdout",
  865. "output_type": "stream",
  866. "text": [
  867. "[(0, {'label': 'C'}), (1, {'label': 'C'}), (2, {'label': 'C'}), (3, {'label': 'C'}), (4, {'label': 'O'})]\n",
  868. " -> \n"
  869. ]
  870. },
  871. {
  872. "data": {
  873. "image/png": "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efJkJkyYQMOGDRk5ciRXX301CcXHyxHxgZp9qrmzzjqLt956i7/+9a8MHTqUK6+8kk8++cTvsKq1jz76iBEjRpCSksKqVavIzs5mzZo1DB06VIlfqhwl/yrMzBg0aBCbNm2iU6dOpKWlcd999/HDDz/4HVq18eOPPzJnzhx69epF7969ad68ORs3bmTGjBl07dpVTTtSZSn5VwMJCQn89a9/Zd26dWzbto327dszY8YMdQ0Nw7fffssjjzxC27ZtefTRRxk2bBg7duzggQceIDk52e/wRMKm5F+NtGzZkunTp/PCCy/w+OOP0717d9auXet3WFXKunXruPHGGzn99NPZvHkzs2bNYsWKFfzud7+jTp06focnEjERSf5mdomZbTGzPDO7J8T6umb2YmD9+2aWEonjSmhF4wPdcMMN9O/fn2HDhrFr1y6/w4pZR44c4cUXX6Rbt24MGDCA008/nY8//pjJkydzzjnn+B2eSFSEnfzNrCYwAegLdAB+Z2bFn0K6EdjjnDsdeAJ4JNzjSulq1qzJjTfeyObNm2nUqBEdO3ZkzJgxHD582O/QYsZXX33FQw89REpKCk8//TR33nkn27dv59577+UUvdREqrlIXPl3AfKcc9udc4eBF4ABxeoMALID868AvUx3yipFUlISjz32GCtWrODtt9/mF7/4BfPnz/c7LN8451i5ciVDhgzhzDPP5IsvvmDRokW89dZbXH755dSqVcvvEEUqRSSSfwvgs6DlzwNlIes4544Ce4EmETi2lFO7du147bXXePLJJ7nrrrvo168fmzdv9jusSnPw4MGf3nk7ePBgOnfuzPbt23nmmWdITU31OzyRShdTN3zNbLiZrTGzNV9//bXf4VRLffv2JScnhz59+tC9e3fuvPNOvvvuO7/DiprPP/+cUaNG0bp1a2bMmMH999/Pxx9/zJ133kmjRo38Dk/EN5FI/l8ALYOWTwuUhaxjZrWAJODb4jtyzk1yzqU559LU5ho9derU4Y9//CMfffQR+/fvp3379jz77LPVZqgI5xzLly/nyiuvpFOnTnz//fcsX76cRYsW0b9/f70sRYTIJP/VwBlm1sbM6gDXAPOK1ZkHXB+YHwS86dQJ3XfNmjVj0qRJLFiwgClTppCWlsa//vUvv8OqsIKCAp599lnOPvtsbr75Znr27MmOHTsYO3Ys//Vf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\n",
  874. "text/plain": [
  875. "<matplotlib.figure.Figure at 0x7f773ca1cc88>"
  876. ]
  877. },
  878. "metadata": {},
  879. "output_type": "display_data"
  880. },
  881. {
  882. "name": "stdout",
  883. "output_type": "stream",
  884. "text": [
  885. "[(0, {'label': 'CC'}), (1, {'label': 'CC'}), (2, {'label': 'CO'}), (3, {'label': 'CCCO'}), (4, {'label': 'OCC'})]\n",
  886. " -> \n"
  887. ]
  888. },
  889. {
  890. "data": {
  891. "image/png": "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\n",
  892. "text/plain": [
  893. "<matplotlib.figure.Figure at 0x7f773c9a44e0>"
  894. ]
  895. },
  896. "metadata": {},
  897. "output_type": "display_data"
  898. },
  899. {
  900. "name": "stdout",
  901. "output_type": "stream",
  902. "text": [
  903. "[(0, {'label': '0'}), (1, {'label': '0'}), (2, {'label': '3'}), (3, {'label': '1'}), (4, {'label': '2'})]\n"
  904. ]
  905. },
  906. {
  907. "data": {
  908. "image/png": "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mslLRKJeZmYm3bp1o27durzwwguUU1tASJrAXURiRtWqVXnjjTc4evQo7du355CGfCgyJX8RKRGSkpKYO3cuDRs2pGnTpuzZs8fvkEo0JX8RKTESEhJ4+umnuf/++2nWrBn//Oc//Q6pxFLyF5ES54EHHmDKlCl07tyZf/zjH36HUyIVKfmb2UVmtsLMdgZ+hhx+z8wuNLMvzOzZohxTRASga9euLF26lAceeICJEyf6HU6JU9Q7/6HAKudcPWBV4HMov8ebqlNEJCwaNmzIhg0bmDZtGgMHDuSHH37wO6QSo6jJvzswI/B+BtAjWCG9GU8+AAAKWklEQVQzawj8BFhexOOJiJylVq1arF+/nnfffZeePXvy3Xff+R1SiVDU5P8T59zewPuv8BL8WcysFPA0MLiIxxIRCapSpUosWbKElJQUWrVqxVdffeV3SFEvz+RvZivN7N0gr+45yznvabFgT4zdByx2zn2Rj2P1N7MtZrYlKysr37+EiEjZsmV58cUX6dq1K02aNOH999/3O6SolufwDs65tqHWmdk+M6vmnNtrZtXwJubJrQnQ3MzuAy4AyprZt865c9oHnHNTgangPeGb319CRATAzBg1ahR16tShdevWzJo1i9atW/sdVlQqarXPAuDOwPs7gddyF3DO3e6cu8w5Vxuv6mdmsMQvIhIud9xxB7NmzaJXr17MnDnT73CiUlGT/3ignZntBNoGPmNmqWY2vajBiYgUVuvWrVm9ejWjR49m7NixROs4Zn7RwG4iEtO++uorunbtytVXX83UqVMpW7as3yFFlAZ2ExEBLrnkEtasWcPhw4fp2LEjhw8f9jukqKDkLyIxr0KFCsybN4/69evTrFkzPvnkE79D8p2Sv4jEhYSEBCZNmkT//v1p1qwZ8V6trOQvInFl4MCBPPfcc3Ts2JGFCxf6HY5vNI2jiMSdHj16UL16dXr06MGnn37KAw884HdIxU53/iISl66//nrWr1/Pc889x6BBg+JuUDglfxGJW3Xq1GHDhg2888473HLLLRw9etTvkIqNkr+IxLWUlBSWLl1KhQoVaN26NZmZwUapiT1K/iIS98qVK8fMmTPp0KEDTZo04cMPP/Q7pIhTg6+ICN6gcI8++ih16tShZcuWzJ07lxYtWvgdVsTozl9EJIf09HReeuklbr75Zl566SW/w4kY3fmL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\n",
  909. "text/plain": [
  910. "<matplotlib.figure.Figure at 0x7f773c9957b8>"
  911. ]
  912. },
  913. "metadata": {},
  914. "output_type": "display_data"
  915. },
  916. {
  917. "name": "stdout",
  918. "output_type": "stream",
  919. "text": [
  920. "[(0, {'label': 'C'}), (1, {'label': 'C'}), (2, {'label': 'C'}), (3, {'label': 'C'}), (4, {'label': 'C'}), (5, {'label': 'C'}), (6, {'label': 'O'})]\n",
  921. " -> \n"
  922. ]
  923. },
  924. {
  925. "data": {
  926. "image/png": "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\n",
  927. "text/plain": [
  928. "<matplotlib.figure.Figure at 0x7f7788e0e390>"
  929. ]
  930. },
  931. "metadata": {},
  932. "output_type": "display_data"
  933. },
  934. {
  935. "name": "stdout",
  936. "output_type": "stream",
  937. "text": [
  938. "[(0, {'label': 'CC'}), (1, {'label': 'CC'}), (2, {'label': 'CC'}), (3, {'label': 'CO'}), (4, {'label': 'CCCC'}), (5, {'label': 'CCCO'}), (6, {'label': 'OCC'})]\n",
  939. " -> \n"
  940. ]
  941. },
  942. {
  943. "data": {
  944. "image/png": "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\n",
  945. "text/plain": [
  946. "<matplotlib.figure.Figure at 0x7f773c95a5f8>"
  947. ]
  948. },
  949. "metadata": {},
  950. "output_type": "display_data"
  951. },
  952. {
  953. "name": "stdout",
  954. "output_type": "stream",
  955. "text": [
  956. "[(0, {'label': '0'}), (1, {'label': '0'}), (2, {'label': '0'}), (3, {'label': '3'}), (4, {'label': '4'}), (5, {'label': '1'}), (6, {'label': '2'})]\n",
  957. "--- shortest path kernel built in 0.00026607513427734375 seconds ---\n",
  958. "6\n"
  959. ]
  960. }
  961. ],
  962. "source": [
  963. "import sys\n",
  964. "import networkx as nx\n",
  965. "sys.path.insert(0, \"../\")\n",
  966. "from pygraph.utils.graphfiles import loadDataset\n",
  967. "from pygraph.kernels.spkernel import spkernel\n",
  968. "\n",
  969. "import matplotlib.pyplot as plt\n",
  970. "\n",
  971. "\n",
  972. "def weisfeilerlehman_test(G):\n",
  973. " '''\n",
  974. " Weisfeiler-Lehman test of graph isomorphism.\n",
  975. " '''\n",
  976. "\n",
  977. " nx.draw_networkx(G)\n",
  978. " plt.show()\n",
  979. " nx.draw_networkx_labels(G, nx.spring_layout(G), labels = nx.get_node_attributes(G,'label'))\n",
  980. " print(G.nodes(data = True))\n",
  981. " \n",
  982. " set_multisets = []\n",
  983. " for node in G.nodes(data = True):\n",
  984. " # Multiset-label determination.\n",
  985. " multiset = [ G.node[neighbors]['label'] for neighbors in G[node[0]] ]\n",
  986. " # sorting each multiset\n",
  987. " multiset.sort()\n",
  988. " multiset = node[1]['label'] + ''.join(multiset) # concatenate to a string and add the prefix \n",
  989. " set_multisets.append(multiset)\n",
  990. " \n",
  991. " # label compression\n",
  992. "# set_multisets.sort() # this is unnecessary\n",
  993. " set_unique = list(set(set_multisets)) # set of unique multiset labels\n",
  994. " set_compressed = { value : str(set_unique.index(value)) for value in set_unique } # assign indices as the new labels\n",
  995. "# print(set_compressed)\n",
  996. "# print(set_multisets)\n",
  997. " \n",
  998. " # relabel nodes with multisets\n",
  999. " for node in G.nodes(data = True):\n",
  1000. " node[1]['label'] = set_multisets[node[0]]\n",
  1001. " print(' -> ')\n",
  1002. " nx.draw_networkx(G)\n",
  1003. " plt.show()\n",
  1004. " print(G.nodes(data = True))\n",
  1005. "\n",
  1006. " \n",
  1007. " # relabel nodes\n",
  1008. " for node in G.nodes(data = True):\n",
  1009. " node[1]['label'] = set_compressed[set_multisets[node[0]]]\n",
  1010. " \n",
  1011. " print(' -> ')\n",
  1012. " nx.draw_networkx(G)\n",
  1013. " plt.show()\n",
  1014. " print(G.nodes(data = True))\n",
  1015. "\n",
  1016. "dataset, y = loadDataset(\"../../../../datasets/acyclic/Acyclic/dataset_bps.ds\")\n",
  1017. "G1 = dataset[12]\n",
  1018. "G2 = dataset[55]\n",
  1019. "\n",
  1020. "# init.\n",
  1021. "kernel = 0 # init kernel\n",
  1022. "num_nodes1 = G1.number_of_nodes()\n",
  1023. "num_nodes2 = G2.number_of_nodes()\n",
  1024. "\n",
  1025. "# the first iteration.\n",
  1026. "labelset1 = { G1.nodes(data = True)[i]['label'] for i in range(num_nodes1) }\n",
  1027. "labelset2 = { G2.nodes(data = True)[i]['label'] for i in range(num_nodes2) }\n",
  1028. "print(labelset1)\n",
  1029. "print(labelset2)\n",
  1030. "kernel += spkernel(G1, G2)\n",
  1031. "print(kernel)\n",
  1032. "\n",
  1033. "\n",
  1034. "\n",
  1035. "for height in range(0, min(num_nodes1, num_nodes2)): #Q how to determine the upper bound of the height?\n",
  1036. " if labelset1 != labelset2:\n",
  1037. " break\n",
  1038. " \n",
  1039. " # Weisfeiler-Lehman test of graph isomorphism.\n",
  1040. " weisfeilerlehman_test(G1)\n",
  1041. " weisfeilerlehman_test(G2)\n",
  1042. " \n",
  1043. " # calculate kernel\n",
  1044. " kernel += spkernel(G1, G2)\n",
  1045. " \n",
  1046. " # get label sets of both graphs\n",
  1047. " labelset1 = { G1.nodes(data = True)[i]['label'] for i in range(num_nodes1) }\n",
  1048. " labelset2 = { G2.nodes(data = True)[i]['label'] for i in range(num_nodes2) }\n",
  1049. "# print(labelset1)\n",
  1050. "# print(labelset2)\n",
  1051. "\n",
  1052. "print(kernel)"
  1053. ]
  1054. },
  1055. {
  1056. "cell_type": "code",
  1057. "execution_count": 20,
  1058. "metadata": {
  1059. "scrolled": false
  1060. },
  1061. "outputs": [
  1062. {
  1063. "name": "stdout",
  1064. "output_type": "stream",
  1065. "text": [
  1066. "{0: 'C', 1: 'C', 2: 'C', 3: 'C', 4: 'C', 5: 'O', 6: 'O'}\n",
  1067. "{0: 'C', 1: 'C', 2: 'C', 3: 'C', 4: 'C', 5: 'C', 6: 'S', 7: 'S'}\n",
  1068. "\n",
  1069. " --- height = 0 --- \n",
  1070. "\n",
  1071. " --- for graph 0 --- \n",
  1072. "\n",
  1073. "labels_ori: ['C', 'C', 'C', 'C', 'C', 'O', 'O']\n",
  1074. "all_labels_ori: {'C', 'O'}\n",
  1075. "num_of_each_label: {'C': 5, 'O': 2}\n",
  1076. "all_num_of_each_label: [{'C': 5, 'O': 2}]\n",
  1077. "num_of_labels: 2\n",
  1078. "all_labels_ori: {'C', 'O'}\n",
  1079. "\n",
  1080. " --- for graph 1 --- \n",
  1081. "\n",
  1082. "labels_ori: ['C', 'C', 'C', 'C', 'C', 'C', 'S', 'S']\n",
  1083. "all_labels_ori: {'C', 'O', 'S'}\n",
  1084. "num_of_each_label: {'C': 6, 'S': 2}\n",
  1085. "all_num_of_each_label: [{'C': 5, 'O': 2}, {'C': 6, 'S': 2}]\n",
  1086. "num_of_labels: 2\n",
  1087. "all_labels_ori: {'C', 'O', 'S'}\n",
  1088. "\n",
  1089. " all_num_of_labels_occured: 3\n",
  1090. "\n",
  1091. " --- calculating kernel matrix ---\n",
  1092. "\n",
  1093. " labels: {'C', 'O'}\n",
  1094. "vector1: [[5 2]]\n",
  1095. "vector2: [[5 2]]\n",
  1096. "Kmatrix: [[ 29. 0.]\n",
  1097. " [ 0. 0.]]\n",
  1098. "\n",
  1099. " labels: {'C', 'O', 'S'}\n",
  1100. "vector1: [[5 2 0]]\n",
  1101. "vector2: [[6 0 2]]\n",
  1102. "Kmatrix: [[ 29. 30.]\n",
  1103. " [ 30. 0.]]\n",
  1104. "\n",
  1105. " labels: {'C', 'S'}\n",
  1106. "vector1: [[6 2]]\n",
  1107. "vector2: [[6 2]]\n",
  1108. "Kmatrix: [[ 29. 30.]\n",
  1109. " [ 30. 40.]]\n",
  1110. "\n",
  1111. " --- height = 1 --- \n",
  1112. "\n",
  1113. " --- for graph 0 --- \n",
  1114. "\n",
  1115. "multiset: ['CC', 'CC', 'CCO', 'CCO', 'COO', 'OCC', 'OCC']\n",
  1116. "set_unique: ['OCC', 'COO', 'CCO', 'CC']\n",
  1117. "set_compressed: {'OCC': '4', 'COO': '5', 'CCO': '6', 'CC': '7'}\n",
  1118. "all_set_compressed: {'OCC': '4', 'COO': '5', 'CCO': '6', 'CC': '7'}\n",
  1119. "num_of_labels_occured: 7\n",
  1120. "\n",
  1121. " compressed labels: {0: '7', 1: '7', 2: '6', 3: '6', 4: '5', 5: '4', 6: '4'}\n",
  1122. "labels_comp: ['7', '7', '6', '6', '5', '4', '4']\n",
  1123. "all_labels_ori: {'5', '4', '6', '7'}\n",
  1124. "num_of_each_label: {'5': 1, '4': 2, '6': 2, '7': 2}\n",
  1125. "all_num_of_each_label: [{'5': 1, '4': 2, '6': 2, '7': 2}]\n",
  1126. "\n",
  1127. " --- for graph 1 --- \n",
  1128. "\n",
  1129. "multiset: ['CC', 'CC', 'CC', 'CCS', 'CCS', 'CCSS', 'SCC', 'SCC']\n",
  1130. "set_unique: ['SCC', 'CC', 'CCS', 'CCSS']\n",
  1131. "set_compressed: {'SCC': '8', 'CC': '7', 'CCS': '9', 'CCSS': '10'}\n",
  1132. "all_set_compressed: {'SCC': '8', 'COO': '5', 'CCS': '9', 'OCC': '4', 'CCO': '6', 'CCSS': '10', 'CC': '7'}\n",
  1133. "num_of_labels_occured: 10\n",
  1134. "\n",
  1135. " compressed labels: {0: '7', 1: '7', 2: '7', 3: '9', 4: '9', 5: '10', 6: '8', 7: '8'}\n",
  1136. "labels_comp: ['7', '7', '7', '9', '9', '10', '8', '8']\n",
  1137. "all_labels_ori: {'10', '4', '7', '9', '6', '5', '8'}\n",
  1138. "num_of_each_label: {'10': 1, '9': 2, '7': 3, '8': 2}\n",
  1139. "all_num_of_each_label: [{'5': 1, '4': 2, '6': 2, '7': 2}, {'10': 1, '9': 2, '7': 3, '8': 2}]\n",
  1140. "\n",
  1141. " all_num_of_labels_occured: 10\n",
  1142. "\n",
  1143. " --- calculating kernel matrix ---\n",
  1144. "\n",
  1145. " labels: {'5', '4', '6', '7'}\n",
  1146. "vector1: [[1 2 2 2]]\n",
  1147. "vector2: [[1 2 2 2]]\n",
  1148. "\n",
  1149. " labels: {'10', '4', '7', '9', '6', '5', '8'}\n",
  1150. "vector1: [[0 2 2 0 2 1 0]]\n",
  1151. "vector2: [[1 0 3 2 0 0 2]]\n",
  1152. "\n",
  1153. " labels: {'8', '10', '7', '9'}\n",
  1154. "vector1: [[2 1 3 2]]\n",
  1155. "vector2: [[2 1 3 2]]\n",
  1156. "\n",
  1157. " Kmatrix: [[ 42. 36.]\n",
  1158. " [ 36. 58.]]\n",
  1159. "\n",
  1160. " --- height = 2 --- \n",
  1161. "\n",
  1162. " --- for graph 0 --- \n",
  1163. "\n",
  1164. "multiset: ['76', '76', '647', '647', '544', '456', '456']\n",
  1165. "set_unique: ['647', '76', '456', '544']\n",
  1166. "set_compressed: {'647': '11', '76': '12', '544': '14', '456': '13'}\n",
  1167. "all_set_compressed: {'647': '11', '76': '12', '456': '13', '544': '14'}\n",
  1168. "num_of_labels_occured: 14\n",
  1169. "\n",
  1170. " compressed labels: {0: '12', 1: '12', 2: '11', 3: '11', 4: '14', 5: '13', 6: '13'}\n",
  1171. "labels_comp: ['12', '12', '11', '11', '14', '13', '13']\n",
  1172. "all_labels_ori: {'14', '12', '11', '13'}\n",
  1173. "num_of_each_label: {'14': 1, '13': 2, '12': 2, '11': 2}\n",
  1174. "all_num_of_each_label: [{'14': 1, '13': 2, '12': 2, '11': 2}]\n",
  1175. "\n",
  1176. " --- for graph 1 --- \n",
  1177. "\n",
  1178. "multiset: ['79', '79', '710', '978', '978', '10788', '8109', '8109']\n",
  1179. "set_unique: ['710', '8109', '79', '10788', '978']\n",
  1180. "set_compressed: {'710': '15', '79': '17', '8109': '16', '978': '19', '10788': '18'}\n",
  1181. "all_set_compressed: {'710': '15', '79': '17', '978': '19', '10788': '18', '8109': '16', '456': '13', '544': '14', '647': '11', '76': '12'}\n",
  1182. "num_of_labels_occured: 19\n",
  1183. "\n",
  1184. " compressed labels: {0: '17', 1: '17', 2: '15', 3: '19', 4: '19', 5: '18', 6: '16', 7: '16'}\n",
  1185. "labels_comp: ['17', '17', '15', '19', '19', '18', '16', '16']\n",
  1186. "all_labels_ori: {'18', '19', '12', '13', '17', '11', '14', '16', '15'}\n",
  1187. "num_of_each_label: {'15': 1, '17': 2, '19': 2, '16': 2, '18': 1}\n",
  1188. "all_num_of_each_label: [{'14': 1, '13': 2, '12': 2, '11': 2}, {'15': 1, '17': 2, '19': 2, '16': 2, '18': 1}]\n",
  1189. "\n",
  1190. " all_num_of_labels_occured: 19\n",
  1191. "\n",
  1192. " --- calculating kernel matrix ---\n",
  1193. "\n",
  1194. " labels: {'14', '12', '11', '13'}\n",
  1195. "vector1: [[1 2 2 2]]\n",
  1196. "vector2: [[1 2 2 2]]\n",
  1197. "\n",
  1198. " labels: {'18', '19', '12', '13', '17', '11', '14', '16', '15'}\n",
  1199. "vector1: [[0 0 2 2 0 2 1 0 0]]\n",
  1200. "vector2: [[1 2 0 0 2 0 0 2 1]]\n",
  1201. "\n",
  1202. " labels: {'18', '17', '15', '16', '19'}\n",
  1203. "vector1: [[1 2 1 2 2]]\n",
  1204. "vector2: [[1 2 1 2 2]]\n",
  1205. "\n",
  1206. " Kmatrix: [[ 55. 36.]\n",
  1207. " [ 36. 72.]]\n",
  1208. "\n",
  1209. " --- Weisfeiler-Lehman subtree kernel built in 0.0034377574920654297 seconds ---\n"
  1210. ]
  1211. },
  1212. {
  1213. "data": {
  1214. "text/plain": [
  1215. "array([[ 55., 36.],\n",
  1216. " [ 36., 72.]])"
  1217. ]
  1218. },
  1219. "execution_count": 20,
  1220. "metadata": {},
  1221. "output_type": "execute_result"
  1222. }
  1223. ],
  1224. "source": [
  1225. "# test of WL subtree kernel on many graphs\n",
  1226. "\n",
  1227. "import sys\n",
  1228. "import pathlib\n",
  1229. "from collections import Counter\n",
  1230. "sys.path.insert(0, \"../\")\n",
  1231. "\n",
  1232. "import networkx as nx\n",
  1233. "import numpy as np\n",
  1234. "import time\n",
  1235. "\n",
  1236. "from pygraph.kernels.spkernel import spkernel\n",
  1237. "from pygraph.kernels.pathKernel import pathkernel\n",
  1238. "\n",
  1239. "def weisfeilerlehmankernel(*args, height = 0, base_kernel = 'subtree'):\n",
  1240. " \"\"\"Calculate Weisfeiler-Lehman kernels between graphs.\n",
  1241. " \n",
  1242. " Parameters\n",
  1243. " ----------\n",
  1244. " Gn : List of NetworkX graph\n",
  1245. " List of graphs between which the kernels are calculated.\n",
  1246. " /\n",
  1247. " G1, G2 : NetworkX graphs\n",
  1248. " 2 graphs between which the kernel is calculated.\n",
  1249. " \n",
  1250. " height : subtree height\n",
  1251. " \n",
  1252. " base_kernel : base kernel used in each iteration of WL kernel\n",
  1253. " the default base kernel is subtree kernel\n",
  1254. " \n",
  1255. " Return\n",
  1256. " ------\n",
  1257. " Kmatrix/Kernel : Numpy matrix/int\n",
  1258. " Kernel matrix, each element of which is the Weisfeiler-Lehman kernel between 2 praphs. / Weisfeiler-Lehman Kernel between 2 graphs.\n",
  1259. " \n",
  1260. " Notes\n",
  1261. " -----\n",
  1262. " This function now supports WL subtree kernel and WL shortest path kernel.\n",
  1263. " \n",
  1264. " References\n",
  1265. " ----------\n",
  1266. " [1] Shervashidze N, Schweitzer P, Leeuwen EJ, Mehlhorn K, Borgwardt KM. Weisfeiler-lehman graph kernels. Journal of Machine Learning Research. 2011;12(Sep):2539-61.\n",
  1267. " \"\"\"\n",
  1268. " if len(args) == 1: # for a list of graphs\n",
  1269. "\n",
  1270. "# print(args)\n",
  1271. " start_time = time.time()\n",
  1272. " \n",
  1273. " # for WL subtree kernel\n",
  1274. " if base_kernel == 'subtree': \n",
  1275. " Kmatrix = _wl_subtreekernel_do(args[0], height = height, base_kernel = 'subtree')\n",
  1276. " \n",
  1277. " # for WL edge kernel\n",
  1278. " elif base_kernel == 'edge':\n",
  1279. " print('edge')\n",
  1280. " \n",
  1281. " # for WL shortest path kernel\n",
  1282. " elif base_kernel == 'sp':\n",
  1283. " Gn = args[0]\n",
  1284. " Kmatrix = np.zeros((len(Gn), len(Gn)))\n",
  1285. " \n",
  1286. " for i in range(0, len(Gn)):\n",
  1287. " for j in range(i, len(Gn)):\n",
  1288. " Kmatrix[i][j] = _weisfeilerlehmankernel_do(Gn[i], Gn[j])\n",
  1289. " Kmatrix[j][i] = Kmatrix[i][j]\n",
  1290. "\n",
  1291. " print(\"\\n --- Weisfeiler-Lehman %s kernel matrix of size %d built in %s seconds ---\" % (base_kernel, len(args[0]), (time.time() - start_time)))\n",
  1292. " \n",
  1293. " return Kmatrix\n",
  1294. " \n",
  1295. " else: # for only 2 graphs\n",
  1296. " \n",
  1297. " start_time = time.time()\n",
  1298. " \n",
  1299. " # for WL subtree kernel\n",
  1300. " if base_kernel == 'subtree':\n",
  1301. " \n",
  1302. " args = [args[0], args[1]]\n",
  1303. "# print(args)\n",
  1304. " kernel = _wl_subtreekernel_do(args, height = height, base_kernel = 'subtree')\n",
  1305. " \n",
  1306. " # for WL edge kernel\n",
  1307. " elif base_kernel == 'edge':\n",
  1308. " print('edge')\n",
  1309. " \n",
  1310. " # for WL shortest path kernel\n",
  1311. " elif base_kernel == 'sp':\n",
  1312. " \n",
  1313. "\n",
  1314. " kernel = _pathkernel_do(args[0], args[1])\n",
  1315. "\n",
  1316. " print(\"\\n --- Weisfeiler-Lehman %s kernel built in %s seconds ---\" % (base_kernel, time.time() - start_time))\n",
  1317. " \n",
  1318. " return kernel\n",
  1319. " \n",
  1320. " \n",
  1321. "def _weisfeilerlehmankernel_do(G1, G2):\n",
  1322. " \"\"\"Calculate Weisfeiler-Lehman kernels between 2 graphs. This kernel use shortest path kernel to calculate kernel between two graphs in each iteration.\n",
  1323. " \n",
  1324. " Parameters\n",
  1325. " ----------\n",
  1326. " G1, G2 : NetworkX graphs\n",
  1327. " 2 graphs between which the kernel is calculated.\n",
  1328. " \n",
  1329. " Return\n",
  1330. " ------\n",
  1331. " Kernel : int\n",
  1332. " Weisfeiler-Lehman Kernel between 2 graphs.\n",
  1333. " \"\"\"\n",
  1334. " \n",
  1335. " # init.\n",
  1336. " kernel = 0 # init kernel\n",
  1337. " num_nodes1 = G1.number_of_nodes()\n",
  1338. " num_nodes2 = G2.number_of_nodes()\n",
  1339. " height = 12 #min(num_nodes1, num_nodes2)) #Q how to determine the upper bound of the height?\n",
  1340. " \n",
  1341. " # the first iteration.\n",
  1342. " labelset1 = { G1.nodes(data = True)[i]['label'] for i in range(num_nodes1) }\n",
  1343. " labelset2 = { G2.nodes(data = True)[i]['label'] for i in range(num_nodes2) }\n",
  1344. " kernel += pathkernel(G1, G2) # change your base kernel here (and one more below)\n",
  1345. " \n",
  1346. " for h in range(0, height):\n",
  1347. "# if labelset1 != labelset2:\n",
  1348. "# break\n",
  1349. "\n",
  1350. " # Weisfeiler-Lehman test of graph isomorphism.\n",
  1351. " relabel(G1)\n",
  1352. " relabel(G2)\n",
  1353. "\n",
  1354. " # calculate kernel\n",
  1355. " kernel += pathkernel(G1, G2) # change your base kernel here (and one more before)\n",
  1356. "\n",
  1357. " # get label sets of both graphs\n",
  1358. " labelset1 = { G1.nodes(data = True)[i]['label'] for i in range(num_nodes1) }\n",
  1359. " labelset2 = { G2.nodes(data = True)[i]['label'] for i in range(num_nodes2) }\n",
  1360. " \n",
  1361. " return kernel\n",
  1362. "\n",
  1363. "\n",
  1364. "def relabel(G):\n",
  1365. " '''\n",
  1366. " Relabel nodes in graph G in one iteration of the 1-dim. WL test of graph isomorphism.\n",
  1367. " \n",
  1368. " Parameters\n",
  1369. " ----------\n",
  1370. " G : NetworkX graph\n",
  1371. " The graphs whose nodes are relabeled.\n",
  1372. " '''\n",
  1373. " \n",
  1374. " # get the set of original labels\n",
  1375. " labels_ori = list(nx.get_node_attributes(G, 'label').values())\n",
  1376. " print(labels_ori)\n",
  1377. " num_of_each_label = dict(Counter(labels_ori))\n",
  1378. " print(num_of_each_label)\n",
  1379. " num_of_labels = len(num_of_each_label)\n",
  1380. " print(num_of_labels)\n",
  1381. " \n",
  1382. " set_multisets = []\n",
  1383. " for node in G.nodes(data = True):\n",
  1384. " # Multiset-label determination.\n",
  1385. " multiset = [ G.node[neighbors]['label'] for neighbors in G[node[0]] ]\n",
  1386. " # sorting each multiset\n",
  1387. " multiset.sort()\n",
  1388. " multiset = node[1]['label'] + ''.join(multiset) # concatenate to a string and add the prefix \n",
  1389. " set_multisets.append(multiset)\n",
  1390. " print(set_multisets)\n",
  1391. " \n",
  1392. " # label compression\n",
  1393. "# set_multisets.sort() # this is unnecessary\n",
  1394. " set_unique = list(set(set_multisets)) # set of unique multiset labels\n",
  1395. " print(set_unique)\n",
  1396. " set_compressed = { value : str(set_unique.index(value) + num_of_labels + 1) for value in set_unique } # assign new labels\n",
  1397. " print(set_compressed)\n",
  1398. " \n",
  1399. " # relabel nodes\n",
  1400. "# nx.relabel_nodes(G, set_compressed, copy = False)\n",
  1401. " for node in G.nodes(data = True):\n",
  1402. " node[1]['label'] = set_compressed[set_multisets[node[0]]]\n",
  1403. " print(nx.get_node_attributes(G, 'label'))\n",
  1404. "\n",
  1405. " # get the set of compressed labels\n",
  1406. " labels_comp = list(nx.get_node_attributes(G, 'label').values())\n",
  1407. " print(labels_comp)\n",
  1408. " num_of_each_label.update(dict(Counter(labels_comp)))\n",
  1409. " print(num_of_each_label)\n",
  1410. " \n",
  1411. " \n",
  1412. "def _wl_subtreekernel_do(*args, height = 0, base_kernel = 'subtree'):\n",
  1413. " \"\"\"Calculate Weisfeiler-Lehman subtree kernels between graphs.\n",
  1414. " \n",
  1415. " Parameters\n",
  1416. " ----------\n",
  1417. " Gn : List of NetworkX graph\n",
  1418. " List of graphs between which the kernels are calculated.\n",
  1419. " \n",
  1420. " Return\n",
  1421. " ------\n",
  1422. " Kmatrix/Kernel : Numpy matrix/int\n",
  1423. " Kernel matrix, each element of which is the Weisfeiler-Lehman kernel between 2 praphs.\n",
  1424. " \"\"\"\n",
  1425. " \n",
  1426. "# print(args)\n",
  1427. " Gn = args[0]\n",
  1428. "# print(Gn)\n",
  1429. "\n",
  1430. " Kmatrix = np.zeros((len(Gn), len(Gn)))\n",
  1431. " all_num_of_labels_occured = 0 # number of the set of letters that occur before as node labels at least once in all graphs\n",
  1432. " \n",
  1433. " # initial for height = 0\n",
  1434. " print('\\n --- height = 0 --- ')\n",
  1435. " all_labels_ori = set() # all unique orignal labels in all graphs in this iteration\n",
  1436. " all_num_of_each_label = [] # number of occurence of each label in each graph in this iteration\n",
  1437. " all_set_compressed = {} # a dictionary mapping original labels to new ones in all graphs in this iteration\n",
  1438. " num_of_labels_occured = all_num_of_labels_occured # number of the set of letters that occur before as node labels at least once in all graphs\n",
  1439. "\n",
  1440. " # for each graph\n",
  1441. " for idx, G in enumerate(Gn):\n",
  1442. " # get the set of original labels\n",
  1443. " print('\\n --- for graph %d --- \\n' % (idx))\n",
  1444. " labels_ori = list(nx.get_node_attributes(G, 'label').values())\n",
  1445. " print('labels_ori: %s' % (labels_ori))\n",
  1446. " all_labels_ori.update(labels_ori)\n",
  1447. " print('all_labels_ori: %s' % (all_labels_ori))\n",
  1448. " num_of_each_label = dict(Counter(labels_ori)) # number of occurence of each label in graph\n",
  1449. " print('num_of_each_label: %s' % (num_of_each_label))\n",
  1450. " all_num_of_each_label.append(num_of_each_label)\n",
  1451. " print('all_num_of_each_label: %s' % (all_num_of_each_label))\n",
  1452. " num_of_labels = len(num_of_each_label) # number of all unique labels\n",
  1453. " print('num_of_labels: %s' % (num_of_labels))\n",
  1454. " \n",
  1455. "\n",
  1456. " all_labels_ori.update(labels_ori)\n",
  1457. " print('all_labels_ori: %s' % (all_labels_ori))\n",
  1458. " \n",
  1459. " all_num_of_labels_occured += len(all_labels_ori)\n",
  1460. " print('\\n all_num_of_labels_occured: %s' % (all_num_of_labels_occured))\n",
  1461. " \n",
  1462. " # calculate subtree kernel with the 0th iteration and add it to the final kernel\n",
  1463. " print('\\n --- calculating kernel matrix ---')\n",
  1464. " for i in range(0, len(Gn)):\n",
  1465. " for j in range(i, len(Gn)):\n",
  1466. " labels = set(list(all_num_of_each_label[i].keys()) + list(all_num_of_each_label[j].keys()))\n",
  1467. " print('\\n labels: %s' % (labels))\n",
  1468. " vector1 = np.matrix([ (all_num_of_each_label[i][label] if (label in all_num_of_each_label[i].keys()) else 0) for label in labels ])\n",
  1469. " vector2 = np.matrix([ (all_num_of_each_label[j][label] if (label in all_num_of_each_label[j].keys()) else 0) for label in labels ])\n",
  1470. " print('vector1: %s' % (vector1))\n",
  1471. " print('vector2: %s' % (vector2))\n",
  1472. " Kmatrix[i][j] += np.dot(vector1, vector2.transpose())\n",
  1473. " Kmatrix[j][i] = Kmatrix[i][j]\n",
  1474. " print('Kmatrix: %s' % (Kmatrix))\n",
  1475. "\n",
  1476. " \n",
  1477. " # iterate each height\n",
  1478. " for h in range(1, height + 1):\n",
  1479. " print('\\n --- height = %d --- ' % (h))\n",
  1480. " all_set_compressed = {} # a dictionary mapping original labels to new ones in all graphs in this iteration\n",
  1481. " num_of_labels_occured = all_num_of_labels_occured # number of the set of letters that occur before as node labels at least once in all graphs\n",
  1482. " all_labels_ori = set()\n",
  1483. " all_num_of_each_label = []\n",
  1484. " \n",
  1485. " # for each graph\n",
  1486. " for idx, G in enumerate(Gn):\n",
  1487. "# # get the set of original labels\n",
  1488. " print('\\n --- for graph %d --- \\n' % (idx))\n",
  1489. "# labels_ori = list(nx.get_node_attributes(G, 'label').values())\n",
  1490. "# print('labels_ori: %s' % (labels_ori))\n",
  1491. "# num_of_each_label = dict(Counter(labels_ori)) # number of occurence of each label in graph\n",
  1492. "# print('num_of_each_label: %s' % (num_of_each_label))\n",
  1493. "# num_of_labels = len(num_of_each_label) # number of all unique labels\n",
  1494. "# print('num_of_labels: %s' % (num_of_labels))\n",
  1495. " \n",
  1496. "# all_labels_ori.update(labels_ori)\n",
  1497. "# print('all_labels_ori: %s' % (all_labels_ori))\n",
  1498. "# # num_of_labels_occured += num_of_labels #@todo not precise\n",
  1499. "# num_of_labels_occured = all_num_of_labels_occured + len(all_labels_ori) + len(all_set_compressed)\n",
  1500. "# print('num_of_labels_occured: %s' % (num_of_labels_occured))\n",
  1501. " \n",
  1502. " set_multisets = []\n",
  1503. " for node in G.nodes(data = True):\n",
  1504. " # Multiset-label determination.\n",
  1505. " multiset = [ G.node[neighbors]['label'] for neighbors in G[node[0]] ]\n",
  1506. " # sorting each multiset\n",
  1507. " multiset.sort()\n",
  1508. " multiset = node[1]['label'] + ''.join(multiset) # concatenate to a string and add the prefix \n",
  1509. " set_multisets.append(multiset)\n",
  1510. " print('multiset: %s' % (set_multisets))\n",
  1511. "\n",
  1512. " # label compression\n",
  1513. " # set_multisets.sort() # this is unnecessary\n",
  1514. " set_unique = list(set(set_multisets)) # set of unique multiset labels\n",
  1515. " print('set_unique: %s' % (set_unique))\n",
  1516. " # a dictionary mapping original labels to new ones. \n",
  1517. " set_compressed = {}\n",
  1518. " # if a label occured before, assign its former compressed label, else assign the number of labels occured + 1 as the compressed label \n",
  1519. " for value in set_unique:\n",
  1520. " if value in all_set_compressed.keys():\n",
  1521. " set_compressed.update({ value : all_set_compressed[value] })\n",
  1522. " else:\n",
  1523. " set_compressed.update({ value : str(num_of_labels_occured + 1) })\n",
  1524. " num_of_labels_occured += 1\n",
  1525. "# set_compressed = { value : (all_set_compressed[value] if value in all_set_compressed.keys() else str(set_unique.index(value) + num_of_labels_occured + 1)) for value in set_unique }\n",
  1526. " print('set_compressed: %s' % (set_compressed))\n",
  1527. " \n",
  1528. " all_set_compressed.update(set_compressed)\n",
  1529. " print('all_set_compressed: %s' % (all_set_compressed))\n",
  1530. "# num_of_labels_occured += len(set_compressed) #@todo not precise\n",
  1531. " print('num_of_labels_occured: %s' % (num_of_labels_occured))\n",
  1532. " \n",
  1533. " # relabel nodes\n",
  1534. " # nx.relabel_nodes(G, set_compressed, copy = False)\n",
  1535. " for node in G.nodes(data = True):\n",
  1536. " node[1]['label'] = set_compressed[set_multisets[node[0]]]\n",
  1537. " print('\\n compressed labels: %s' % (nx.get_node_attributes(G, 'label')))\n",
  1538. "\n",
  1539. " # get the set of compressed labels\n",
  1540. " labels_comp = list(nx.get_node_attributes(G, 'label').values())\n",
  1541. " print('labels_comp: %s' % (labels_comp))\n",
  1542. " all_labels_ori.update(labels_comp)\n",
  1543. " print('all_labels_ori: %s' % (all_labels_ori))\n",
  1544. " num_of_each_label = dict(Counter(labels_comp))\n",
  1545. " print('num_of_each_label: %s' % (num_of_each_label))\n",
  1546. " all_num_of_each_label.append(num_of_each_label)\n",
  1547. " print('all_num_of_each_label: %s' % (all_num_of_each_label))\n",
  1548. " \n",
  1549. " all_num_of_labels_occured += len(all_labels_ori)\n",
  1550. " print('\\n all_num_of_labels_occured: %s' % (all_num_of_labels_occured))\n",
  1551. " \n",
  1552. " # calculate subtree kernel with h iterations and add it to the final kernel\n",
  1553. " print('\\n --- calculating kernel matrix ---')\n",
  1554. " for i in range(0, len(Gn)):\n",
  1555. " for j in range(i, len(Gn)):\n",
  1556. " labels = set(list(all_num_of_each_label[i].keys()) + list(all_num_of_each_label[j].keys()))\n",
  1557. " print('\\n labels: %s' % (labels))\n",
  1558. " vector1 = np.matrix([ (all_num_of_each_label[i][label] if (label in all_num_of_each_label[i].keys()) else 0) for label in labels ])\n",
  1559. " vector2 = np.matrix([ (all_num_of_each_label[j][label] if (label in all_num_of_each_label[j].keys()) else 0) for label in labels ])\n",
  1560. " print('vector1: %s' % (vector1))\n",
  1561. " print('vector2: %s' % (vector2))\n",
  1562. " Kmatrix[i][j] += np.dot(vector1, vector2.transpose())\n",
  1563. " Kmatrix[j][i] = Kmatrix[i][j]\n",
  1564. " \n",
  1565. " print('\\n Kmatrix: %s' % (Kmatrix))\n",
  1566. "\n",
  1567. " return Kmatrix\n",
  1568. "\n",
  1569. " \n",
  1570. "# main\n",
  1571. "import sys\n",
  1572. "from collections import Counter\n",
  1573. "import networkx as nx\n",
  1574. "sys.path.insert(0, \"../\")\n",
  1575. "from pygraph.utils.graphfiles import loadDataset\n",
  1576. "from pygraph.kernels.spkernel import spkernel\n",
  1577. "\n",
  1578. "dataset, y = loadDataset(\"../../../../datasets/acyclic/Acyclic/dataset_bps.ds\")\n",
  1579. "G1 = dataset[15]\n",
  1580. "print(nx.get_node_attributes(G1, 'label'))\n",
  1581. "G2 = dataset[80]\n",
  1582. "print(nx.get_node_attributes(G2, 'label'))\n",
  1583. "\n",
  1584. "weisfeilerlehmankernel(G1, G2, height = 2)\n",
  1585. "# Kmatrix = weisfeilerlehmankernel(G1, G2)"
  1586. ]
  1587. },
  1588. {
  1589. "cell_type": "code",
  1590. "execution_count": 4,
  1591. "metadata": {},
  1592. "outputs": [
  1593. {
  1594. "data": {
  1595. "text/plain": [
  1596. "185"
  1597. ]
  1598. },
  1599. "execution_count": 4,
  1600. "metadata": {},
  1601. "output_type": "execute_result"
  1602. }
  1603. ],
  1604. "source": [
  1605. "\n",
  1606. "len(dataset)"
  1607. ]
  1608. },
  1609. {
  1610. "cell_type": "code",
  1611. "execution_count": 1,
  1612. "metadata": {
  1613. "scrolled": true
  1614. },
  1615. "outputs": [
  1616. {
  1617. "name": "stdout",
  1618. "output_type": "stream",
  1619. "text": [
  1620. "\n",
  1621. "- This script take as input a kernel matrix\n",
  1622. "and returns the classification or regression performance\n",
  1623. "- The kernel matrix can be calculated using any of the graph kernels approaches\n",
  1624. "- The criteria used for prediction are SVM for classification and kernel Ridge regression for regression\n",
  1625. "- For predition we divide the data in training, validation and test. For each split, we first train on the train data, \n",
  1626. "then evaluate the performance on the validation. We choose the optimal parameters for the validation set and finally\n",
  1627. "provide the corresponding performance on the test set. If more than one split is performed, the final results \n",
  1628. "correspond to the average of the performances on the test sets. \n",
  1629. "\n",
  1630. "@references\n",
  1631. " https://github.com/eghisu/GraphKernels/blob/master/GraphKernelsCollection/python_scripts/compute_perf_gk.py\n",
  1632. "\n",
  1633. "\n",
  1634. " --- calculating kernel matrix when subtree height = 0 ---\n",
  1635. "\n",
  1636. " Loading dataset from file...\n",
  1637. "[ -23.7 14. 37.3 109.7 10.8 39. 42. 66.6 135. 148.5\n",
  1638. " 40. 34.6 32. 63. 53.5 67. 64.4 84.7 95.5 92.\n",
  1639. " 84.4 154. 156. 166. 183. 70.3 63.6 52.5 59. 59.5\n",
  1640. " 55.2 88. 83. 104.5 102. 92. 107.4 123.2 112.5 118.5\n",
  1641. " 101.5 173.7 165.5 181. 99.5 92.3 90.1 80.2 82. 91.2\n",
  1642. " 91.5 81.2 93. 69. 86.3 82. 103. 103.5 96. 112. 104.\n",
  1643. " 132.5 123.5 120.3 145. 144.2 142.8 132. 134.2 137. 139.\n",
  1644. " 133.6 120.4 120. 137. 195.8 177.2 181. 185.9 175.7 186. 211.\n",
  1645. " 125. 118. 117.1 107. 102.5 112. 97.4 91.5 87.6 106.5\n",
  1646. " 101. 99.3 90. 137. 114. 126. 124. 140.5 157.5 146. 145.\n",
  1647. " 141. 171. 166. 155. 145. 159. 138. 142. 159. 163.5\n",
  1648. " 229.5 142. 125. 132. 130.5 125. 122. 121. 122.2 112. 106.\n",
  1649. " 114.5 151. 128.5 109.5 126. 147. 158. 147. 165. 188.9\n",
  1650. " 170. 178. 148.5 165. 177. 167. 195. 226. 215. 201. 205.\n",
  1651. " 151.5 165.5 157. 139. 163. 153.5 139. 162. 173. 159.5\n",
  1652. " 159.5 155.5 141. 126. 164. 163. 166.5 146. 165. 159. 195.\n",
  1653. " 218. 250. 235. 186.5 156.5 162. 162. 170.2 173.2 186.8\n",
  1654. " 173. 187. 174. 188.5 199. 228. 215. 216. 240. ]\n",
  1655. "\n",
  1656. " --- This is a regression problem ---\n",
  1657. "\n",
  1658. " Calculating kernel matrix, this could take a while...\n"
  1659. ]
  1660. },
  1661. {
  1662. "ename": "KeyboardInterrupt",
  1663. "evalue": "",
  1664. "output_type": "error",
  1665. "traceback": [
  1666. "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
  1667. "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)",
  1668. "\u001b[0;32m<ipython-input-1-2ce8cff340bc>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 82\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 83\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'\\n Calculating kernel matrix, this could take a while...'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 84\u001b[0;31m \u001b[0mKmatrix\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mweisfeilerlehmankernel\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdataset\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mheight\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheight\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbase_kernel\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'sp'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 85\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mKmatrix\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 86\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'\\n Saving kernel matrix to file...'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
  1669. "\u001b[0;32m/media/ljia/DATA/research-repo/codes/Linlin/py-graph/pygraph/kernels/weisfeilerLehmanKernel.py\u001b[0m in \u001b[0;36mweisfeilerlehmankernel\u001b[0;34m(height, base_kernel, *args)\u001b[0m\n\u001b[1;32m 71\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mi\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mGn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 72\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0mj\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mrange\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mGn\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 73\u001b[0;31m \u001b[0mKmatrix\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mj\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_weisfeilerlehmankernel_do\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mGn\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mGn\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mj\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mheight\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mheight\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 74\u001b[0m \u001b[0mKmatrix\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mj\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mKmatrix\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mj\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 75\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
  1670. "\u001b[0;32m/media/ljia/DATA/research-repo/codes/Linlin/py-graph/pygraph/kernels/weisfeilerLehmanKernel.py\u001b[0m in \u001b[0;36m_weisfeilerlehmankernel_do\u001b[0;34m(G1, G2, height)\u001b[0m\n\u001b[1;32m 241\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 242\u001b[0m \u001b[0;31m# calculate kernel\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 243\u001b[0;31m \u001b[0mkernel\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0mspkernel\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mG1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mG2\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# change your base kernel here (and one more before)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 244\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 245\u001b[0m \u001b[0;31m# get label sets of both graphs\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
  1671. "\u001b[0;32m/media/ljia/DATA/research-repo/codes/Linlin/py-graph/pygraph/kernels/spkernel.py\u001b[0m in \u001b[0;36mspkernel\u001b[0;34m(*args)\u001b[0m\n\u001b[1;32m 62\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0me1\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mG1\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0medges\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[0;32mfor\u001b[0m \u001b[0me2\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mG2\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0medges\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 64\u001b[0;31m \u001b[0;32mif\u001b[0m \u001b[0me1\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'cost'\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0;36m0\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0me1\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'cost'\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0me2\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'cost'\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0me1\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0me2\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0me1\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0me2\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0me1\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0me2\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0me1\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0me2\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 65\u001b[0m \u001b[0mkernel\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 66\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
  1672. "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
  1673. ]
  1674. }
  1675. ],
  1676. "source": [
  1677. "# Author: Elisabetta Ghisu\n",
  1678. "# test of WL subtree kernel\n",
  1679. "\n",
  1680. "\"\"\"\n",
  1681. "- This script take as input a kernel matrix\n",
  1682. "and returns the classification or regression performance\n",
  1683. "- The kernel matrix can be calculated using any of the graph kernels approaches\n",
  1684. "- The criteria used for prediction are SVM for classification and kernel Ridge regression for regression\n",
  1685. "- For predition we divide the data in training, validation and test. For each split, we first train on the train data, \n",
  1686. "then evaluate the performance on the validation. We choose the optimal parameters for the validation set and finally\n",
  1687. "provide the corresponding performance on the test set. If more than one split is performed, the final results \n",
  1688. "correspond to the average of the performances on the test sets. \n",
  1689. "\n",
  1690. "@references\n",
  1691. " https://github.com/eghisu/GraphKernels/blob/master/GraphKernelsCollection/python_scripts/compute_perf_gk.py\n",
  1692. "\"\"\"\n",
  1693. "\n",
  1694. "print(__doc__)\n",
  1695. "\n",
  1696. "import sys\n",
  1697. "import os\n",
  1698. "import pathlib\n",
  1699. "sys.path.insert(0, \"../\")\n",
  1700. "from tabulate import tabulate\n",
  1701. "\n",
  1702. "import random\n",
  1703. "import numpy as np\n",
  1704. "import matplotlib.pyplot as plt\n",
  1705. "\n",
  1706. "from sklearn.kernel_ridge import KernelRidge # 0.17\n",
  1707. "from sklearn.metrics import accuracy_score, mean_squared_error\n",
  1708. "from sklearn import svm\n",
  1709. "\n",
  1710. "from pygraph.kernels.weisfeilerLehmanKernel import weisfeilerlehmankernel\n",
  1711. "from pygraph.utils.graphfiles import loadDataset\n",
  1712. "\n",
  1713. "val_means_height = []\n",
  1714. "val_stds_height = []\n",
  1715. "test_means_height = []\n",
  1716. "test_stds_height = []\n",
  1717. "\n",
  1718. "\n",
  1719. "for height in np.linspace(0, 10, 11):\n",
  1720. " print('\\n --- calculating kernel matrix when subtree height = %d ---' % height)\n",
  1721. "\n",
  1722. " print('\\n Loading dataset from file...')\n",
  1723. " dataset, y = loadDataset(\"../../../../datasets/acyclic/Acyclic/dataset_bps.ds\")\n",
  1724. " y = np.array(y)\n",
  1725. " print(y)\n",
  1726. "\n",
  1727. " # setup the parameters\n",
  1728. " model_type = 'regression' # Regression or classification problem\n",
  1729. " print('\\n --- This is a %s problem ---' % model_type)\n",
  1730. "\n",
  1731. " datasize = len(dataset)\n",
  1732. " trials = 100 # Trials for hyperparameters random search\n",
  1733. " splits = 10 # Number of splits of the data\n",
  1734. " alpha_grid = np.logspace(-10, 10, num = trials, base = 10) # corresponds to (2*C)^-1 in other linear models such as LogisticRegression\n",
  1735. " C_grid = np.logspace(-10, 10, num = trials, base = 10)\n",
  1736. " random.seed(20) # Set the seed for uniform parameter distribution\n",
  1737. "\n",
  1738. " # set the output path\n",
  1739. " kernel_file_path = 'kernelmatrices_weisfeilerlehman_acyclic/'\n",
  1740. " if not os.path.exists(kernel_file_path):\n",
  1741. " os.makedirs(kernel_file_path)\n",
  1742. "\n",
  1743. "\n",
  1744. " \"\"\"\n",
  1745. " - Here starts the main program\n",
  1746. " - First we permute the data, then for each split we evaluate corresponding performances\n",
  1747. " - In the end, the performances are averaged over the test sets\n",
  1748. " \"\"\"\n",
  1749. "\n",
  1750. " # save kernel matrices to files / read kernel matrices from files\n",
  1751. " kernel_file = kernel_file_path + 'km.ds'\n",
  1752. " path = pathlib.Path(kernel_file)\n",
  1753. " # get train set kernel matrix\n",
  1754. " if path.is_file():\n",
  1755. " print('\\n Loading the kernel matrix from file...')\n",
  1756. " Kmatrix = np.loadtxt(kernel_file)\n",
  1757. " print(Kmatrix)\n",
  1758. " else:\n",
  1759. " print('\\n Calculating kernel matrix, this could take a while...')\n",
  1760. " Kmatrix = weisfeilerlehmankernel(dataset, node_label = 'atom', height = int(height), base_kernel = 'sp')\n",
  1761. " print(Kmatrix)\n",
  1762. " print('\\n Saving kernel matrix to file...')\n",
  1763. "# np.savetxt(kernel_file, Kmatrix)\n",
  1764. "\n",
  1765. " # Initialize the performance of the best parameter trial on validation with the corresponding performance on test\n",
  1766. " val_split = []\n",
  1767. " test_split = []\n",
  1768. "\n",
  1769. " # For each split of the data\n",
  1770. " for j in range(10, 10 + splits):\n",
  1771. " # print('\\n Starting split %d...' % j)\n",
  1772. "\n",
  1773. " # Set the random set for data permutation\n",
  1774. " random_state = int(j)\n",
  1775. " np.random.seed(random_state)\n",
  1776. " idx_perm = np.random.permutation(datasize)\n",
  1777. " # print(idx_perm)\n",
  1778. "\n",
  1779. " # Permute the data\n",
  1780. " y_perm = y[idx_perm] # targets permutation\n",
  1781. " # print(y_perm)\n",
  1782. " Kmatrix_perm = Kmatrix[:, idx_perm] # inputs permutation\n",
  1783. " # print(Kmatrix_perm)\n",
  1784. " Kmatrix_perm = Kmatrix_perm[idx_perm, :] # inputs permutation\n",
  1785. "\n",
  1786. " # Set the training, validation and test\n",
  1787. " # Note: the percentage can be set up by the user\n",
  1788. " num_train_val = int((datasize * 90) / 100) # 90% (of entire dataset) for training and validation\n",
  1789. " num_test = datasize - num_train_val # 10% (of entire dataset) for test\n",
  1790. " num_train = int((num_train_val * 90) / 100) # 90% (of train + val) for training\n",
  1791. " num_val = num_train_val - num_train # 10% (of train + val) for validation\n",
  1792. "\n",
  1793. " # Split the kernel matrix\n",
  1794. " Kmatrix_train = Kmatrix_perm[0:num_train, 0:num_train]\n",
  1795. " Kmatrix_val = Kmatrix_perm[num_train:(num_train + num_val), 0:num_train]\n",
  1796. " Kmatrix_test = Kmatrix_perm[(num_train + num_val):datasize, 0:num_train]\n",
  1797. "\n",
  1798. " # Split the targets\n",
  1799. " y_train = y_perm[0:num_train]\n",
  1800. "\n",
  1801. " # Normalization step (for real valued targets only)\n",
  1802. " if model_type == 'regression':\n",
  1803. " # print('\\n Normalizing output y...')\n",
  1804. " y_train_mean = np.mean(y_train)\n",
  1805. " y_train_std = np.std(y_train)\n",
  1806. " y_train = (y_train - y_train_mean) / float(y_train_std)\n",
  1807. " # print(y)\n",
  1808. "\n",
  1809. " y_val = y_perm[num_train:(num_train + num_val)]\n",
  1810. " y_test = y_perm[(num_train + num_val):datasize]\n",
  1811. "\n",
  1812. " # Record the performance for each parameter trial respectively on validation and test set\n",
  1813. " perf_all_train = []\n",
  1814. " perf_all_test = []\n",
  1815. "\n",
  1816. " # For each parameter trial\n",
  1817. " for i in range(trials):\n",
  1818. " # For regression use the Kernel Ridge method\n",
  1819. " if model_type == 'regression':\n",
  1820. " # print('\\n Starting experiment for trial %d and parameter alpha = %3f\\n ' % (i, alpha_grid[i]))\n",
  1821. "\n",
  1822. " # Fit the kernel ridge model\n",
  1823. " KR = KernelRidge(kernel = 'precomputed', alpha = alpha_grid[i])\n",
  1824. " # KR = svm.SVR(kernel = 'precomputed', C = C_grid[i])\n",
  1825. " KR.fit(Kmatrix_train, y_train)\n",
  1826. "\n",
  1827. " # predict on the validation and test set\n",
  1828. " y_pred = KR.predict(Kmatrix_val)\n",
  1829. " y_pred_test = KR.predict(Kmatrix_test)\n",
  1830. " # print(y_pred)\n",
  1831. "\n",
  1832. " # adjust prediction: needed because the training targets have been normalizaed\n",
  1833. " y_pred = y_pred * float(y_train_std) + y_train_mean\n",
  1834. " # print(y_pred)\n",
  1835. " y_pred_test = y_pred_test * float(y_train_std) + y_train_mean\n",
  1836. " # print(y_pred_test)\n",
  1837. "\n",
  1838. " # root mean squared error on validation\n",
  1839. " rmse = np.sqrt(mean_squared_error(y_val, y_pred))\n",
  1840. " perf_all_val.append(rmse)\n",
  1841. "\n",
  1842. " # root mean squared error in test \n",
  1843. " rmse_test = np.sqrt(mean_squared_error(y_test, y_pred_test))\n",
  1844. " perf_all_test.append(rmse_test)\n",
  1845. "\n",
  1846. " # print('The performance on the validation set is: %3f' % rmse)\n",
  1847. " # print('The performance on the test set is: %3f' % rmse_test)\n",
  1848. "\n",
  1849. " # --- FIND THE OPTIMAL PARAMETERS --- #\n",
  1850. " # For regression: minimise the mean squared error\n",
  1851. " if model_type == 'regression':\n",
  1852. "\n",
  1853. " # get optimal parameter on validation (argmin mean squared error)\n",
  1854. " min_idx = np.argmin(perf_all_test)\n",
  1855. " alpha_opt = alpha_grid[min_idx]\n",
  1856. "\n",
  1857. " # performance corresponding to optimal parameter on val\n",
  1858. " perf_val_opt = perf_all_val[min_idx]\n",
  1859. "\n",
  1860. " # corresponding performance on test for the same parameter\n",
  1861. " perf_test_opt = perf_all_test[min_idx]\n",
  1862. "\n",
  1863. " # print('The best performance is for trial %d with parameter alpha = %3f' % (min_idx, alpha_opt))\n",
  1864. " # print('The best performance on the validation set is: %3f' % perf_val_opt)\n",
  1865. " # print('The corresponding performance on test set is: %3f' % perf_test_opt)\n",
  1866. "\n",
  1867. " # append the best performance on validation\n",
  1868. " # at the current split\n",
  1869. " val_split.append(perf_val_opt)\n",
  1870. "\n",
  1871. " # append the correponding performance on the test set\n",
  1872. " test_split.append(perf_test_opt)\n",
  1873. "\n",
  1874. " # average the results\n",
  1875. " # mean of the validation performances over the splits\n",
  1876. " val_mean = np.mean(np.asarray(val_split))\n",
  1877. " # std deviation of validation over the splits\n",
  1878. " val_std = np.std(np.asarray(val_split))\n",
  1879. "\n",
  1880. " # mean of the test performances over the splits\n",
  1881. " test_mean = np.mean(np.asarray(test_split))\n",
  1882. " # std deviation of the test oer the splits\n",
  1883. " test_std = np.std(np.asarray(test_split))\n",
  1884. "\n",
  1885. " print('\\n Mean performance on val set: %3f' % val_mean)\n",
  1886. " print('With standard deviation: %3f' % val_std)\n",
  1887. " print('\\n Mean performance on test set: %3f' % test_mean)\n",
  1888. " print('With standard deviation: %3f' % test_std)\n",
  1889. " \n",
  1890. " val_means_height.append(val_mean)\n",
  1891. " val_stds_height.append(val_std)\n",
  1892. " test_means_height.append(test_mean)\n",
  1893. " test_stds_height.append(test_std)\n",
  1894. " \n",
  1895. "print('\\n') \n",
  1896. "print(tabulate({'height': np.linspace(1, 12, 11), 'RMSE': test_means_height, 'std': test_stds_height}, headers='keys'))"
  1897. ]
  1898. },
  1899. {
  1900. "cell_type": "code",
  1901. "execution_count": 15,
  1902. "metadata": {},
  1903. "outputs": [
  1904. {
  1905. "data": {
  1906. "text/plain": [
  1907. "{0: 'C', 1: 'C', 2: 'C', 3: 'C', 4: 'C', 5: 'O', 6: 'O'}"
  1908. ]
  1909. },
  1910. "execution_count": 15,
  1911. "metadata": {},
  1912. "output_type": "execute_result"
  1913. }
  1914. ],
  1915. "source": [
  1916. "# a = [0, 1, 3, 2]\n",
  1917. "# b = [3, 2, 1, 0]\n",
  1918. "# print(1 if a == b else 0)\n",
  1919. "\n",
  1920. "# max(1 ,2)\n",
  1921. "\n",
  1922. "# x = [ 'r', 'a', 's' ]\n",
  1923. "# x.sort()\n",
  1924. "# print(x)\n",
  1925. "\n",
  1926. "# def test1(*args, base = 'subtree'):\n",
  1927. "# if base == 'subtree':\n",
  1928. "# print('subtree')\n",
  1929. "# elif base == 'edge':\n",
  1930. "# print('edge')\n",
  1931. "# else:\n",
  1932. "# print('sp')\n",
  1933. "\n",
  1934. "# # function parameter usage test\n",
  1935. "# test1('hello', 'hi', base = 'edge')\n",
  1936. "\n",
  1937. "# # python matrix calculation speed test\n",
  1938. "# import numpy as np\n",
  1939. "# import time\n",
  1940. "\n",
  1941. "# size = 100\n",
  1942. "# m1 = np.random.random((size, size))\n",
  1943. "# m2 = np.random.random((size, size))\n",
  1944. "# itr = 1\n",
  1945. "\n",
  1946. "# start_time = time.time()\n",
  1947. "# for i in range(itr):\n",
  1948. "# np.dot(m1, m2)\n",
  1949. "# print(time.time() - start_time)\n",
  1950. "\n",
  1951. "# start_time = time.time()\n",
  1952. "# for j in range(itr):\n",
  1953. "# result = np.zeros((size, size))\n",
  1954. "# for i1 in range(size):\n",
  1955. "# for i2 in range(size):\n",
  1956. "# for i3 in range(size):\n",
  1957. "# result[i1][i2] += m1[i1][i3] * m2[i3][i2]\n",
  1958. "# print(time.time() - start_time)\n",
  1959. "\n",
  1960. "# start_time = time.time()\n",
  1961. "# for i in range(itr):\n",
  1962. "# print(np.dot(m1, m2))\n",
  1963. "# print(time.time() - start_time)\n",
  1964. "\n",
  1965. "# start_time = time.time()\n",
  1966. "# for j in range(itr):\n",
  1967. "# result = np.zeros((size, size))\n",
  1968. "# for i1 in range(size):\n",
  1969. "# for i2 in range(size):\n",
  1970. "# for i3 in range(size):\n",
  1971. "# result[i1][i2] += m1[i1][i3] * m2[i3][i2]\n",
  1972. "# print(result)\n",
  1973. "# print(time.time() - start_time)\n",
  1974. "\n",
  1975. "# help(np.sum)\n",
  1976. "\n",
  1977. "# test dict\n",
  1978. "import sys\n",
  1979. "from collections import Counter\n",
  1980. "import networkx as nx\n",
  1981. "sys.path.insert(0, \"../\")\n",
  1982. "from pygraph.utils.graphfiles import loadDataset\n",
  1983. "from pygraph.kernels.spkernel import spkernel\n",
  1984. "\n",
  1985. "dataset, y = loadDataset(\"../../../../datasets/acyclic/Acyclic/dataset_bps.ds\")\n",
  1986. "G1 = dataset[15]\n",
  1987. "nx.get_node_attributes(G1, 'label')\n",
  1988. "listhqhq = list(nx.get_node_attributes(G1, 'label').values())\n",
  1989. "dicthaha = dict(Counter(listhqhq))\n",
  1990. "len(dicthaha)"
  1991. ]
  1992. }
  1993. ],
  1994. "metadata": {
  1995. "kernelspec": {
  1996. "display_name": "Python 3",
  1997. "language": "python",
  1998. "name": "python3"
  1999. },
  2000. "language_info": {
  2001. "codemirror_mode": {
  2002. "name": "ipython",
  2003. "version": 3
  2004. },
  2005. "file_extension": ".py",
  2006. "mimetype": "text/x-python",
  2007. "name": "python",
  2008. "nbconvert_exporter": "python",
  2009. "pygments_lexer": "ipython3",
  2010. "version": "3.5.2"
  2011. }
  2012. },
  2013. "nbformat": 4,
  2014. "nbformat_minor": 2
  2015. }

A Python package for graph kernels, graph edit distances and graph pre-image problem.