├── CNN.py ├── GA.py └── README.md /CNN.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | import input_data 4 | import tensorflow as tf 5 | 6 | mnist = input_data.read_data_sets("MNIST_data/", one_hot=True) #读取图片数据集 7 | sess = tf.InteractiveSession() 8 | 9 | #---------------------------重要函数声明------------------------------------- 10 | #权重变量 11 | def weight_variable(shape): 12 | #输出正态分布的随机值,标准差为0.1,默认最大为1,最小为-1,均值为0 13 | inital = tf.truncated_normal(shape, stddev=0.1) 14 | return tf.Variable(inital) 15 | 16 | #偏移变量 17 | def bias_veriable(shape): 18 | #创建一个结构为shape的矩阵,或者说数组,声明其行和列,初始化所有值为0.1 19 | inital = tf.constant(0.1, shape=shape) 20 | return tf.Variable(inital) 21 | 22 | #卷积函数,大概意思是根据步长一步一步去遍历输入的值,行成特征向量输出到下一层 23 | def conv2d(x, W): 24 | #卷积遍历各个方向步数为1,SAME表示边缘自动补0而不是丢弃,遍历相乘 25 | #strides:表示步长:当输入的默认格式为:“NHWC”, 26 | #则 strides = [batch , in_height , in_width, in_channels]。 27 | #其中 batch 和 in_channels 要求一定为1,即只能在一个样本的一个通道上的特征图上进行移动, 28 | #in_height , in_width表示卷积核在特征图的高度和宽度上移动的布长 29 | return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME') 30 | 31 | #池化函数:意思是把输入的高宽变小,比如4*4 变成2* 2,有两种方式,一种是取每一个小块的最大值,另外一种是取每一个小块的平均值 32 | def max_pool_2_2(x): 33 | #池化卷积结果(conv2d),池化层的kernal采用和2*2的大小,步数也为2,周围填0,取最大值,数量量缩小 34 | return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME') 35 | 36 | #--------------------------定义输入输出结构--------------------------------- 37 | #声明一个占位符,None表示输入的图片数量不定,28*28的图片分辨率 38 | xs = tf.placeholder(tf.float32, [None, 784]) 39 | #输出类别总共是0-9个类别,对应分类的输出结果 40 | ys = tf.placeholder(tf.float32, [None, 10]) 41 | 42 | #x_image又把xs reshape成 28*28*1的形状,因为是灰色图片,所以通道是1,作为训练时的input,-1代表图片数量不定 43 | x_image = tf.reshape(xs, [-1, 28, 28, 1]) 44 | 45 | #-------------------------搭建网络,定义算法公式,也就是前进时候具体应该怎么计算---------- 46 | 47 | #第一层卷积操作 48 | #第一次卷积核的大小,即patch,第三个参数是图像通道数,第四个参数是卷积核数目,代表会出现多少个卷积特征图像 49 | W_conv1 = weight_variable([5, 5, 1, 32]) 50 | b_conv1 = bias_veriable([32]) #偏移b的数量应该和w一致 51 | #图片乘以卷积核加上偏移量,卷积结果为28 * 28 * 32 52 | #relu函数表示一个激活函数,在大于0时激活,激活到第一象限,其他的收敛于一个接近于0的小数 53 | h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) 54 | 55 | #结果池化,变成14*14*32的池化结果作为下一层的输入 56 | h_pool1 = max_pool_2_2(h_conv1) 57 | 58 | #第二次卷积 59 | #32个通道卷积出64个特征 60 | w_conv2 = weight_variable([5, 5, 32, 64]) 61 | #64个偏移数据 62 | b_conv2 = bias_veriable([64]) 63 | #注意上一层池化的结果作为输入 64 | h_conv2 = tf.nn.relu(conv2d(h_pool1, w_conv2) + b_conv2) 65 | #池化结果 66 | h_pool2 = max_pool_2_2(h_conv2) 67 | #原始图像尺寸为28*28,第一轮图像缩小为14*14共32张,第二轮继续缩小为7*7共64张 68 | 69 | #第三层全连接操作 70 | #二维张量,第一个参数7*7*64的patch,第二个参数代表卷积有1024个 71 | W_fc1 = weight_variable([7*7*64, 1024]) 72 | #1024个偏执量 73 | b_fc1 = bias_veriable([1024]) 74 | #将第二层的池化结果reshape成一行,有7*7*64个数据[n, 7, 7, 64]->[n, 7*7*64] 75 | h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64]) 76 | #卷积操作,结果是1*1*1024,这里只有一行,所以直接采用矩阵相乘,而不是遍历相乘,第一个参数是行向量,第二个是列向量 77 | h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1) 78 | 79 | #drop操作,为了减少过度拟合,降低上一层某些输入的权值,防止测评曲线出现震荡 80 | #使用占位符,由dropout自动确定scale 81 | keep_prob = tf.placeholder(tf.float32) 82 | #对卷积结果进行drop操作 83 | h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob) 84 | 85 | #第四层输出操作 86 | #二维张量,1*1024矩阵卷积,对应我们定义的10个输出长度ys 87 | W_fc2 = weight_variable([1024, 10]) 88 | tf.histogram_summary('outputlayer/W', W_fc2) 89 | b_fc2 = bias_veriable([10]) 90 | tf.histogram_summary('outputlayer/b', b_fc2) 91 | 92 | #结果层用逻辑回归softmax或者sigmoid 93 | y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2) 94 | 95 | #定义代价函数loss,选定优化方法优化loss 96 | """ 97 | 交叉熵可在神经网络(机器学习)中作为损失函数,p表示真实标记的分布,q则为训练后的模型的预测标记分布,交叉熵损失函数可以衡量p与q的相似性。 98 | 交叉熵作为损失函数还有一个好处是使用sigmoid函数在梯度下降时能避免均方误差损失函数学习速率降低的问题,因为学习速率可以被输出的误差所控制。 99 | """ 100 | 101 | #这里使用交叉熵为损失函数 102 | cross_entroy = -tf.reduce_sum(ys*tf.log(tf.clip_by_value(y_conv,1e-10,1.0))) #-tf.reduce_sum(ys * tf.log(y_conv)) 103 | 104 | #使用tf写好的梯度下降作为优化函数,0.5表示迭代速率,minimize表示希望交叉熵最小 105 | train_step = tf.train.AdagradOptimizer(0.001).minimize(cross_entroy) 106 | 107 | #开始数据训练及评测 108 | #这句表示训练结果一组求最大值之后和实际结果是否相等 109 | correct_perdiction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(ys, 1)) 110 | #这句表示将上面的结果向量转换成float型,然后求平均而已 111 | arrcuray = tf.reduce_mean(tf.cast(correct_perdiction, tf.float32)) 112 | 113 | init_op = tf.initialize_all_variables() 114 | sess.run(init_op) 115 | 116 | #可视化 117 | tf.scalar_summary("cross_entroy", cross_entroy) 118 | tf.scalar_summary("arrcuray", arrcuray) 119 | 120 | merged_summary_op = tf.merge_all_summaries() 121 | summary_writer = tf.train.SummaryWriter('CNN.logs', sess.graph_def) 122 | 123 | #迭代20000次 124 | for i in range(2000): 125 | #每次取长度为50的一个数据片段 126 | batch = mnist.train.next_batch(50) 127 | 128 | # 执行训练 129 | # train_step.run(feed_dict={xs: batch[0], ys: batch[1], keep_prob: 0.5}) 130 | sess.run(train_step, feed_dict={xs: batch[0], ys: batch[1], keep_prob: 0.5}) 131 | 132 | #每迭代100次输出一次结果 133 | if i%100 == 0: 134 | #打印参数,填充之前的定义的占位符 135 | train_accuray = arrcuray.eval(feed_dict={xs:batch[0], ys:batch[1], keep_prob: 1.0}) 136 | print("step %d, training accuracy %g" % (i, train_accuray)) 137 | summaryStr = sess.run(merged_summary_op, feed_dict={xs:batch[0], ys:batch[1], keep_prob: 1.0}) 138 | summary_writer.add_summary(summaryStr, i) 139 | 140 | #打印测试结果 141 | print("test accuracy %g" % arrcuray.eval(feed_dict={xs: mnist.test.images, ys: mnist.test.labels, keep_prob: 1.0})) 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /GA.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | """ 4 | 问题: 5 | 6 | 从某物流中心用多台配送车辆向多个客户送货,每个客户的位置和货物需求量一定,每台配送车辆的载重量一定,其一次配送的最大行驶距离一定,要求合理安排车辆配送路线,使目标函数得到优化,并满足以下条件: 7 | 8 | (1) 每条配送路径上各客户的需求量之和不超过配送车辆的载重量; 9 | 10 | (2) 每条配送路径的长度不超过配送车辆一次配送的最大行驶距离; 11 | 12 | (3) 每个客户的需求必须满足,且只能由一台配送车辆送货。 13 | 14 | 以配送总里程最短为目标函数 15 | """ 16 | 17 | """ 18 | 一个实例: 19 | 20 | 某物流中心有2 台配送车辆,其载重量均为8t ,车辆每次配送的最大行驶距离为50km ,配送中心(其编号为0) 与8 个客户之间及8 个客户相互之间的距离dij 、8 个客户的货物需求量qj (i 、j = 1 ,2 , ⋯,8) 均见表1 。要求合理安排车辆配送路线,使配送总里程最短。 21 | 采用以下参数:群体规模取20 ,进化代数取25 ,交叉概率取0.9 ,变异概率取0.09 ,变异时基因换位次数取5 , 对不可行路径的惩罚权重取100km ,实施爬山操作时爬山次数取20 。对实例随机求解10 次。 22 | 23 | """ 24 | 25 | import random 26 | 27 | #遗传算法 28 | class GeneticAlgorithm: 29 | 30 | #-----------初始数据定义--------------------- 31 | #定义一个9 * 9的二维数组表示配送中心(编号为0)与8个客户之间,以及8个客户相互之间的距离d[i][j] 32 | d = [[0, 4, 6, 7.5, 9, 20, 10, 16, 8], #配送中心(编号为0)到8个客户送货点的距离 33 | [4, 0, 6.5, 4, 10, 5, 7.5, 11, 10], #第1个客户到配送中心和其他8个客户送货点的距离 34 | [6, 6.5, 0, 7.5, 10, 10, 7.5, 7.5, 7.5], #第2个客户到配送中心和其他8个客户送货点的距离 35 | [7.5, 4, 7.5, 0, 10, 5, 9, 9, 15], 36 | [9, 10, 10, 10, 0, 10, 7.5, 7.5, 10 ], 37 | [20, 5, 10, 5, 10, 0, 7, 9, 7.5], 38 | [10, 7.5, 7.5, 9, 7.5, 7, 0, 7, 10], 39 | [16, 11, 7.5, 9, 7.5, 9, 7, 0, 10], 40 | [8, 10, 7.5, 15, 10, 7.5, 10, 10, 0]]; 41 | 42 | # 8个客户分布需要的货物的需求量,第0位为配送中心自己 43 | q = [0, 1, 2, 1, 2, 1, 4, 2, 2]; 44 | 45 | #定义一些遗传算法需要的参数 46 | JCL = 0.9 #遗传时的交叉率 47 | BYL = 0.09 #遗传时的变异率 48 | JYHW = 5 #变异时的基因换位次数 49 | PSCS = 20 #爬山算法时的迭代次数 50 | 51 | def __init__(self, rows, times, mans, cars, tons, distance, PW): 52 | self.rows = rows #排列个数 53 | self.times = times #迭代次数 54 | self.mans = mans #客户数量 55 | self.cars = cars #车辆总数 56 | self.tons = tons #车辆载重 57 | self.distance = distance #车辆一次行驶的最大距离 58 | self.PW = PW #当生成一个不可行路线时的惩罚因子 59 | 60 | #-------------遗传函数开始执行--------------------- 61 | def run(self): 62 | 63 | print "开始迭代" 64 | 65 | #路线数组 66 | lines = [[0 for i in range(self.mans)] for i in range(self.rows)] 67 | 68 | #适应度 69 | fit = [0 for i in range(self.rows)] 70 | 71 | # print "初始输入获取rows个随机排列,并且计算适应度" 72 | #初始输入获取rows个随机排列,并且计算适应度 73 | j = 0 74 | for i in range(0, self.rows): 75 | j = 0 76 | while j < self.mans: 77 | num = int(random.uniform(0, self.mans)) + 1 78 | if self.isHas(lines[i], num) == False: 79 | lines[i][j] = num 80 | j += 1 81 | 82 | #计算每个线路的适应度 83 | # print "计算每个线路的适应度 i = %d" % (i) 84 | fit[i] = self.calFitness(lines[i], False) 85 | 86 | #迭代次数 87 | t = 0 88 | 89 | while t < self.times: 90 | 91 | #适应度 92 | newLines = [[0 for i in range(self.mans)] for i in range(self.rows)] 93 | nextFit = [0 for i in range(self.rows)] 94 | randomFit = [0 for i in range(self.rows)] 95 | totalFit = 0 96 | tmpFit = 0 97 | 98 | # print "计算总的适应度" 99 | #计算总的适应度 100 | for i in range(self.rows): 101 | totalFit += fit[i] 102 | 103 | # print "通过适应度占总适应度的比例生成随机适应度" 104 | #通过适应度占总适应度的比例生成随机适应度 105 | for i in range(self.rows): 106 | randomFit[i] = tmpFit + fit[i] / totalFit 107 | tmpFit += randomFit[i] 108 | 109 | # print "上一代中的最优直接遗传到下一代" 110 | #上一代中的最优直接遗传到下一代 111 | m = fit[0] 112 | ml = 0 113 | 114 | for i in range(self.rows): 115 | if m < fit[i]: 116 | m = fit[i] 117 | ml = i 118 | 119 | for i in range(self.mans): 120 | newLines[0][i] = lines[ml][i] 121 | 122 | nextFit[0] = fit[ml] 123 | 124 | # print "对最优解使用爬山算法促使其自我进化" 125 | #对最优解使用爬山算法促使其自我进化 126 | self.clMountain(newLines[0]) 127 | 128 | # print "开始遗传" 129 | #开始遗传 130 | nl = 1 131 | while nl < self.rows: 132 | #根据概率选取排列 133 | r = int(self.randomSelect(randomFit)) 134 | 135 | #判断是否需要交叉,不能越界 136 | if random.random() < self.JCL and nl + 1 < self.rows: 137 | fline = [0 for x in range(self.mans)] 138 | nline = [0 for x in range(self.mans)] 139 | 140 | #获取交叉排列 141 | rn = int(self.randomSelect(randomFit)) 142 | 143 | f = int(random.uniform(0, self.mans)) 144 | l = int(random.uniform(0, self.mans)) 145 | 146 | min = 0 147 | max = 0 148 | fpo = 0 149 | npo = 0 150 | 151 | if f < l: 152 | min = f 153 | max = l 154 | else: 155 | min = l 156 | max = f 157 | 158 | # print "将截取的段加入新生成的基因" 159 | #将截取的段加入新生成的基因 160 | """ 161 | 除排在第一位的最优个体外,另N - 1 个个体要按交叉概率Pc 进行配对交叉重组。 162 | 采用类OX法实施交叉操作,现举例说明其操作方法: 163 | ①随机在父代个体中选择一个交配区域,如两父代个体及交配区域选定为:A = 47| 8563| 921 ,B = 83| 4691|257 ; 164 | ②将B 的交配区域加到A 的前面,A 的交配区域加到B 的前面,得:A’= 4691| 478563921 ,B’=8563| 834691257 ; 165 | ③在A’、B’中自交配区域后依次删除与交配区相同的自然数,得到最终的两个体为:A”= 469178532 ,B”= 856349127 166 | 167 | """ 168 | while min < max: 169 | fline[fpo] = lines[rn][min] 170 | nline[npo] = lines[r][min] 171 | 172 | min += 1 173 | fpo += 1 174 | npo += 1 175 | 176 | for i in range(self.mans): 177 | if self.isHas(fline, lines[r][i]) == False: 178 | fline[fpo] = lines[r][i] 179 | fpo += 1 180 | 181 | if self.isHas(nline, lines[rn][i]) == False: 182 | nline[npo] = lines[rn][i] 183 | npo += 1 184 | 185 | #基因变异 186 | self.change(fline) 187 | self.change(nline) 188 | 189 | # print "交叉并且变异后的结果加入下一代" 190 | #交叉并且变异后的结果加入下一代 191 | for i in range(self.mans): 192 | newLines[nl][i] = fline[i] 193 | newLines[nl + 1][i] = nline[i] 194 | 195 | nextFit[nl] = self.calFitness(fline, False) 196 | nextFit[nl + 1] = self.calFitness(nline, False) 197 | 198 | nl += 2 199 | else: 200 | # print "不需要交叉的,直接变异,然后遗传到下一代" 201 | #不需要交叉的,直接变异,然后遗传到下一代 202 | 203 | line = [0 for i in range(self.mans)] 204 | i = 0 205 | while i < self.mans: 206 | line[i] = lines[r][i] 207 | i += 1 208 | 209 | #基因变异 210 | self.change(line) 211 | 212 | #加入下一代 213 | i = 0 214 | while i < self.mans: 215 | newLines[nl][i] = line[i] 216 | i += 1 217 | 218 | nextFit[nl] = self.calFitness(line, False) 219 | nl += 1 220 | 221 | # print "新的一代覆盖上一代 当前是第 %d 代" %(t) 222 | #新的一代覆盖上一代 223 | for i in range(self.rows): 224 | for h in range(self.mans): 225 | lines[i][h] = newLines[i][h] 226 | 227 | fit[i] = nextFit[i] 228 | 229 | t += 1 230 | 231 | #上代中最优的为适应函数最小的 232 | m = fit[0] 233 | ml = 0 234 | 235 | for i in range(self.rows): 236 | if m < fit[i]: 237 | m = fit[i] 238 | ml = i 239 | 240 | print "迭代完成" 241 | #输出结果: 242 | self.calFitness(lines[ml], True) 243 | 244 | print "最优权值为: %f" %(m) 245 | print "最优结果为:" 246 | 247 | for i in range(self.mans): 248 | print "%d," %(lines[ml][i]), 249 | 250 | print " " 251 | print " " 252 | print " " 253 | 254 | 255 | #-----------------遗传函数执行完成-------------------- 256 | 257 | #-----------------各种辅助计算函数-------------------- 258 | #线路中是否包含当前的客户 259 | def isHas(self, line, num): 260 | for i in range(0, self.mans): 261 | if line[i] == num: 262 | return True 263 | return False 264 | 265 | 266 | #计算适应度,适应度计算的规则为每条配送路径要满足题设条件,并且目标函数即 车辆行驶的总里程越小,适应度越高 267 | def calFitness(self, line, isShow): 268 | 269 | carTon = 0 #当前车辆的载重 270 | carDis = 0 #当前车辆行驶的总距离 271 | newTon = 0 272 | newDis = 0 273 | totalDis = 0 274 | 275 | # ll = [] 276 | r = 0 #表示当前需要车辆数 277 | # l = 0 278 | fore = 0 #表示正在运送的客户编号 279 | M = 0 #表示当前的路径规划所需要的总车辆和总共拥有的车辆之间的差,如果大于0,表示是一个失败的规划,乘以一个很大的惩罚因子用来降低适应度 280 | 281 | #遍历每个客户点 282 | for i in range(0, self.mans): 283 | #行驶的距离 284 | newDis = carDis + self.d[fore][line[i]] 285 | 286 | #当前车辆的载重 287 | newTon = carTon + self.q[line[i]] 288 | 289 | #如果已经超过最大行驶距离或者超过车辆的最大载重,切换到下一辆车 290 | if newDis + self.d[line[i]][0] > self.distance or newTon > self.tons: 291 | #下一辆车 292 | totalDis += carDis + self.d[fore][0] #后面加这个d[fore][0]表示需要从当前客户处返程的距离 293 | r += 1 294 | fore = 0 295 | i -= 1 #表示当前这个点的配送还没有完成 296 | carTon = 0 297 | carDis = 0 298 | else: 299 | carDis = newDis 300 | carTon = newTon 301 | fore = line[i] 302 | 303 | #加上最后一辆车的距离和返程的距离 304 | totalDis += carDis + self.d[fore][0] 305 | 306 | if isShow: 307 | print "总行驶里程为: %.1fkm" %(totalDis) 308 | else: 309 | # print "中间过程尝试规划的总行驶里程为: %.1fkm" %(totalDis) 310 | pass 311 | 312 | #判断路径是否可用,所使用的车辆数量不能大于总车辆数量 313 | if r - self.cars + 1 > 0: 314 | M = r - self.cars + 1 315 | 316 | #目标函数,表示一个路径规划行驶的总距离的倒数越小越好 317 | result = 1 / (totalDis + M * self.PW) 318 | 319 | return result 320 | 321 | 322 | #爬山算法 323 | def clMountain(self, line): 324 | oldFit = self.calFitness(line, False) 325 | 326 | i = 0 327 | while i < self.PSCS: 328 | f = random.uniform(0, self.mans) 329 | n = random.uniform(0, self.mans) 330 | 331 | self.doChange(line, f, n) 332 | 333 | newFit = self.calFitness(line, False) 334 | 335 | if newFit < oldFit: 336 | self.doChange(line, f, n) 337 | i += 1 338 | 339 | #基因变异 340 | #变异的意思是当满足变异率的条件下,随机的两个因子发生多次交换,交换次数为变异迭代次数规定的次数 341 | def change(self, line): 342 | if random.random() < self.BYL: 343 | i = 0 344 | while i < self.JYHW: 345 | f = random.uniform(0, self.mans) 346 | n = random.uniform(0, self.mans) 347 | 348 | self.doChange(line, f, n) 349 | i += 1 350 | 351 | 352 | #将线路中的两个因子执行交换 353 | def doChange(self, line, f, n): 354 | 355 | tmp = line[int(f)] 356 | line[int(f)] = line[int(n)] 357 | line[int(n)] = tmp 358 | 359 | #根据概率随机选择的序列 360 | def randomSelect(self, ranFit): 361 | 362 | ran = random.random() 363 | 364 | for i in range(self.rows): 365 | if ran < ranFit[i]: 366 | return i 367 | 368 | 369 | #-------------入口函数,开始执行----------------------------- 370 | 371 | """ 372 | 输入参数的的意义依次为 373 | 374 | self.rows = rows #排列个数 375 | self.times = times #迭代次数 376 | self.mans = mans #客户数量 377 | self.cars = cars #车辆总数 378 | self.tons = tons #车辆载重 379 | self.distance = distance #车辆一次行驶的最大距离 380 | self.PW = PW #当生成一个不可行路线时的惩罚因子 381 | 382 | """ 383 | ga = GeneticAlgorithm(rows=20, times=25, mans=8, cars=2, tons=8, distance=50, PW=100) 384 | 385 | for i in range(20): 386 | print "第 %d 次:" %(i + 1) 387 | ga.run() 388 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GeneticAlgorithm 2 | 3 | 用混合遗传算法求解物流配送路径: 4 | 5 | 问题: 6 | 7 | 从某物流中心用多台配送车辆向多个客户送货,每个客户的位置和货物需求量一定,每台配送车辆的载重量一定,其一次配送的最大行驶距离一定,要求合理安排车辆配送路线,使目标函数得到优化,并满足以下条件: 8 | 9 | (1) 每条配送路径上各客户的需求量之和不超过配送车辆的载重量; 10 | 11 | (2) 每条配送路径的长度不超过配送车辆一次配送的最大行驶距离; 12 | 13 | (3) 每个客户的需求必须满足,且只能由一台配送车辆送货。 14 | 15 | 使用Python实现 16 | --------------------------------------------------------------------------------