diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..ef047b429ef06bb99b6a2939f12a647e418ba10e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+*.jpg
diff --git a/name.txt b/name.txt
new file mode 100644
index 0000000000000000000000000000000000000000..62bbd9cdc9822f7ebe3f36f9c6d1aaa9950caafa
--- /dev/null
+++ b/name.txt
@@ -0,0 +1,2 @@
+法品哲
+2022265233
\ No newline at end of file
diff --git a/report_02_Titanic/.idea/.gitignore b/report_02_Titanic/.idea/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..26d33521af10bcc7fd8cea344038eaaeb78d0ef5
--- /dev/null
+++ b/report_02_Titanic/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/report_02_Titanic/.idea/inspectionProfiles/profiles_settings.xml b/report_02_Titanic/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..105ce2da2d6447d11dfe32bfb846c3d5b199fc99
--- /dev/null
+++ b/report_02_Titanic/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/report_02_Titanic/.idea/misc.xml b/report_02_Titanic/.idea/misc.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b153a8ca3aa2bc618dcb29eb3dc8153d472f98ab
--- /dev/null
+++ b/report_02_Titanic/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/report_02_Titanic/.idea/modules.xml b/report_02_Titanic/.idea/modules.xml
new file mode 100644
index 0000000000000000000000000000000000000000..40c768577017f3142b36a49016bfd7a782edd841
--- /dev/null
+++ b/report_02_Titanic/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/report_02_Titanic/.idea/report_02_Titanic.iml b/report_02_Titanic/.idea/report_02_Titanic.iml
new file mode 100644
index 0000000000000000000000000000000000000000..ef04e02d395c90b10e2dbc63c921bdb6cbbfb61b
--- /dev/null
+++ b/report_02_Titanic/.idea/report_02_Titanic.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/report_02_Titanic/__pycache__/treePlotter.cpython-38.pyc b/report_02_Titanic/__pycache__/treePlotter.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..76053dd1cd7ce6f3707354797b8d81acd4b55f9c
Binary files /dev/null and b/report_02_Titanic/__pycache__/treePlotter.cpython-38.pyc differ
diff --git a/report_02_Titanic/detc_tree.py b/report_02_Titanic/detc_tree.py
new file mode 100644
index 0000000000000000000000000000000000000000..c935f66a372b7bfd1204c7b4a6e80f20902c9010
--- /dev/null
+++ b/report_02_Titanic/detc_tree.py
@@ -0,0 +1,206 @@
+from math import log
+
+# 输入参数 str 需要判断的字符串
+# 返回值 True:该字符串为浮点数;False:该字符串不是浮点数。
+def IsFloatNum(str):
+ s=str.split('.')
+ if len(s)>2:
+ return False
+ else:
+ for si in s:
+ if not si.isdigit():
+ return False
+ return True
+
+# 构造数据集
+def create_dataset():
+ with open("train.csv",mode='r') as f:
+ file=f.readlines()
+ for i,l in enumerate(file):
+ file[i] = l.split(',')
+ file[i].pop(3)
+ file[i].pop(0)
+ label = file[i].pop(0)
+ for j,c in enumerate(file[i]):
+ file[i][j] = c.split('\n')[0]
+ if IsFloatNum(c):
+ file[i][j] = float(c)
+ if i > 0:
+ file[i] += [int(label)]
+ dataset = file[1:]
+ features = file[0]
+ return dataset, features
+
+
+# 计算信息熵
+def compute_entropy(dataset):
+ # 求总样本数
+ num_of_examples = len(dataset)
+ labelCnt = {}
+ # 遍历整个样本集合
+ for example in dataset:
+ # 当前样本的标签值是该列表的最后一个元素
+ currentLabel = example[-1]
+ # 统计每个标签各出现了几次
+ if currentLabel not in labelCnt.keys():
+ labelCnt[currentLabel] = 0
+ labelCnt[currentLabel] += 1
+ entropy = 0.0
+ # 对于原样本集,labelCounts = {'no': 6, 'yes': 9}
+ # 对应的初始shannonEnt = (-6/15 * log(6/15)) + (- 9/15 * log(9/15))
+ for key in labelCnt:
+ p = labelCnt[key] / num_of_examples
+ entropy -= p * log(p, 2)
+ return entropy
+
+
+# 提取子集合
+# 功能:从dataSet中先找到所有第axis个标签值 = value的样本
+# 然后将这些样本删去第axis个标签值,再全部提取出来成为一个新的样本集
+def create_sub_dataset(dataset, index, value):
+ sub_dataset = []
+ for example in dataset:
+ current_list = []
+ if example[index] == value:
+ current_list = example[:index]
+ current_list.extend(example[index + 1:])
+ sub_dataset.append(current_list)
+ return sub_dataset
+
+
+def choose_best_feature(dataset):
+ num_of_features = len(dataset[0]) - 1
+ # 计算当前数据集的信息熵
+ current_entropy = compute_entropy(dataset)
+ # 初始化信息增益率
+ best_information_gain_ratio = 0.0
+ # 初始化最佳特征的下标为-1
+ index_of_best_feature = -1
+ # 通过下标遍历整个特征列表
+ for i in range(num_of_features):
+ # 构造所有样本在当前特征的取值的列表
+ values_of_current_feature = [example[i] for example in dataset]
+ unique_values = set(values_of_current_feature)
+ # 初始化新的信息熵
+ new_entropy = 0.0
+ # 初始化分离信息
+ split_info = 0.0
+ for value in unique_values:
+ # print(len(sub_dataset))
+ # print(len(dataset))
+ sub_dataset = create_sub_dataset(dataset, i, value)
+ p = len(sub_dataset) / (len(dataset)+1e-7)
+ # 计算使用该特征进行样本划分后的新信息熵
+ new_entropy += p * compute_entropy(sub_dataset)
+ # 计算分离信息
+ split_info -= p * log(p, 2)
+ # print(1)
+ # 计算信息增益
+ # information_gain = current_entropy - new_entropy
+ # 计算信息增益率(Gain_Ratio = Gain / Split_Info)
+ # print(i)
+ information_gain_ratio = (current_entropy - new_entropy) / split_info
+ # 求出最大的信息增益及对应的特征下标
+ if information_gain_ratio > best_information_gain_ratio:
+ best_information_gain_ratio = information_gain_ratio
+ index_of_best_feature = i
+ # 这里返回的是特征的下标
+ return index_of_best_feature
+
+
+# 返回具有最多样本数的那个标签的值('yes' or 'no')
+def find_label(classList):
+ # 初始化统计各标签次数的字典
+ # 键为各标签,对应的值为标签出现的次数
+ labelCnt = {}
+ for key in classList:
+ if key not in labelCnt.keys():
+ labelCnt[key] = 0
+ labelCnt[key] += 1
+ # 将classCount按值降序排列
+ # 例如:sorted_labelCnt = {'yes': 9, 'no': 6}
+ sorted_labelCnt = sorted(labelCnt.items(), key=lambda a: a[1], reverse=True)
+ # 下面这种写法有问题
+ # sortedClassCount = sorted(labelCnt.iteritems(), key=operator.itemgetter(1), reverse=True)
+ # 取sorted_labelCnt中第一个元素中的第一个值,即为所求
+ return sorted_labelCnt[0][0]
+
+
+def create_decision_tree(dataset, features):
+ label_list = [example[-1] for example in dataset]
+ # 先写两个递归结束的情况:
+ # 若当前集合的所有样本标签相等(即样本已被分“纯”)
+ # 则直接返回该标签值作为一个叶子节点
+ if label_list.count(label_list[0]) == len(label_list):
+ return label_list[0]
+ # 若训练集的所有特征都被使用完毕,当前无可用特征,但样本仍未被分“纯”
+ # 则返回所含样本最多的标签作为结果
+ if len(dataset[0]) == 1:
+ return find_label(label_list)
+ # 下面是正式建树的过程
+ # 选取进行分支的最佳特征的下标
+ index_of_best_feature = choose_best_feature(dataset)
+ # 得到最佳特征
+ best_feature = features[index_of_best_feature]
+ # 初始化决策树
+ decision_tree = {best_feature: {}}
+ # 使用过当前最佳特征后将其删去
+ del (features[index_of_best_feature])
+ # 取出各样本在当前最佳特征上的取值列表
+ values_of_best_feature = [example[index_of_best_feature] for example in dataset]
+ # 用set()构造当前最佳特征取值的不重复集合
+ unique_values = set(values_of_best_feature)
+ # 对于uniqueVals中的每一个取值
+ for value in unique_values:
+ # 子特征 = 当前特征(因为刚才已经删去了用过的特征)
+ sub_features = features[:]
+ # 递归调用create_decision_tree去生成新节点
+ decision_tree[best_feature][value] = create_decision_tree(
+ create_sub_dataset(dataset, index_of_best_feature, value), sub_features)
+ return decision_tree
+
+
+# 用上面训练好的决策树对新样本分类
+def classify(decision_tree, features, test_example):
+ # 根节点代表的属性
+ first_feature = list(decision_tree.keys())[0]
+ # second_dict是第一个分类属性的值(也是字典)
+ second_dict = decision_tree[first_feature]
+ # 树根代表的属性,所在属性标签中的位置,即第几个属性
+ index_of_first_feature = features.index(first_feature)
+ # 对于second_dict中的每一个key
+ for key in second_dict.keys():
+ if test_example[index_of_first_feature] == key:
+ # 若当前second_dict的key的value是一个字典
+ if type(second_dict[key]).__name__ == 'dict':
+ # 则需要递归查询
+ classLabel = classify(second_dict[key], features, test_example)
+ # 若当前second_dict的key的value是一个单独的值
+ else:
+ # 则就是要找的标签值
+ classLabel = second_dict[key]
+ return classLabel
+
+
+if __name__ == '__main__':
+ dataset, features = create_dataset()
+ decision_tree = create_decision_tree(dataset, features)
+ # 打印生成的决策树
+ print("生成决策树:",decision_tree)
+ # 对新样本进行分类测试
+ with open("test.csv",mode='r') as f:
+ file=f.readlines()
+ for i,l in enumerate(file):
+ file[i] = l.split(',')
+ file[i].pop(2)
+ file[i].pop(0)
+ for j,c in enumerate(file[i]):
+ file[i][j] = c.split('\n')[0]
+ if IsFloatNum(c):
+ file[i][j] = float(c)
+ index = 10
+ test_example = file[index]
+ features = file[0]
+ print("\n"+str(index)+'号人员是否存活:', bool(classify(decision_tree, features, test_example)))
+
+#%%
diff --git a/report_02_Titanic/pre_cut.py b/report_02_Titanic/pre_cut.py
new file mode 100644
index 0000000000000000000000000000000000000000..008eac6d935ad947174e3fe6dff65895314b9782
--- /dev/null
+++ b/report_02_Titanic/pre_cut.py
@@ -0,0 +1,57 @@
+import numpy as np
+
+def treePostPruning(labeledTree, dataValid, labelValid, feats):
+ labelValidSet = {}
+ newTree = labeledTree.copy()
+ dataValid = np.asarray(dataValid)
+ labelValid = np.asarray(labelValid)
+ feats = np.asarray(feats)
+ featName = list(labeledTree.keys())[0]
+ featCol = np.argwhere(feats == featName)[0][0]
+ feats = np.delete(feats, [featCol])
+ newTree[featName] = labeledTree[featName].copy()
+ featValueDict = newTree[featName]
+ featPreLabel = featValueDict.pop("_vpdl")
+ # print("当前节点预划分标签:" + featPreLabel)
+ # 是否为子树的标记
+ subTreeFlag = 0
+ # 分割测试数据 如果有数据 则进行测试或递归调用 np的array我不知道怎么判断是否None, 用is None是错的
+ dataFlag = 1 if sum(dataValid.shape) > 0 else 0
+ if dataFlag == 1:
+ # print("当前节点有划分数据!")
+ dataValidSet, labelValidSet = splitFeatureData(dataValid, labelValid, featCol)
+ for featValue in featValueDict.keys():
+ # print("当前节点属性 {0} 的子节点:{1}".format(featValue ,str(featValueDict[featValue])))
+ if dataFlag == 1 and type(featValueDict[featValue]) == dict:
+ subTreeFlag = 1
+ # 如果是子树则递归
+ newTree[featName][featValue] = treePostPruning(featValueDict[featValue], dataValidSet.get(featValue),
+ labelValidSet.get(featValue), feats)
+ # 如果递归后为叶子 则后续进行评估
+ if type(featValueDict[featValue]) != dict:
+ subTreeFlag = 0
+
+ # 如果没有数据 则转换子树
+ if dataFlag == 0 and type(featValueDict[featValue]) == dict:
+ subTreeFlag = 1
+ # print("当前节点无划分数据!直接转换树:"+str(featValueDict[featValue]))
+ newTree[featName][featValue] = convertTree(featValueDict[featValue])
+ # print("转换结果:" + str(convertTree(featValueDict[featValue])))
+ # 如果全为叶子节点, 评估需要划分前的标签,这里思考两种方法,
+ # 一是,不改变原来的训练函数,评估时使用训练数据对划分前的节点标签重新打标
+ # 二是,改进训练函数,在训练的同时为每个节点增加划分前的标签,这样可以保证评估时只使用测试数据,避免再次使用大量的训练数据
+ # 这里考虑第二种方法 写新的函数 createTreeWithLabel,当然也可以修改createTree来添加参数实现
+ if subTreeFlag == 0:
+ ratioPreDivision = equalNums(labelValid, featPreLabel) / labelValid.size
+ equalNum = 0
+ for val in labelValidSet.keys():
+ if val in featValueDict:
+ equalNum += equalNums(labelValidSet[val], featValueDict[val])
+ else:
+ equalNum += len(labelValidSet[val])/5 # 一共五类,随便选一类
+ ratioAfterDivision = equalNum / labelValid.size
+ # 如果划分后的测试数据准确率低于划分前的,则划分无效,进行剪枝,即使节点等于预划分标签
+ # 注意这里取的是小于,如果有需要 也可以取 小于等于
+ if ratioAfterDivision < ratioPreDivision:
+ newTree = featPreLabel
+ return newTree
\ No newline at end of file
diff --git a/report_02_Titanic/report_template.ipynb b/report_02_Titanic/report_template.ipynb
index 21eb1ce711b96fd98ffe44be0b1697fee849508a..374fabbe1d6f2cc36bbd9caf1c1e88c2938bd0f9 100644
--- a/report_02_Titanic/report_template.ipynb
+++ b/report_02_Titanic/report_template.ipynb
@@ -4,32 +4,66 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Report - 报告题目\n",
+ "# Report - Titanic\n",
"\n",
- "* 姓名\n",
- "* 学号\n",
+ "* 法品哲\n",
+ "* 2022265233\n",
"\n",
"\n",
"## 任务简介\n",
"\n",
- "这里简述一下任务是什么;数据的格式,包含了什么数据;最终的目标是什么\n",
+ "根据泰坦尼克号上人物的身份信息判断其最终是否获救,数据组织格式为csv表格,每个人的信息由一个一维数组表示;数据中存在大量缺失值。\n",
"\n",
"## 解决途径\n",
- "\n",
- "主要包括:\n",
- "1. 问题的思考,整体的思路\n",
- "2. 选用的方法,以及为何选用这些方法\n",
- "3. 实现过程遇到的问题,以及如何解决的\n",
- "4. 最终的结果,实验分析\n",
- "\n",
- "要求:\n",
- "1. 数据的可视化\n",
- "2. 程序,以及各个部分的解释、说明\n",
- "3. 结果的可视化,精度等的分析\n",
+ "1.考虑到是个二分类问题,每个单位的属性十分清晰,或为割裂可数的离散值属性(如性别、出发港口、船票等级),或为封闭区间的连续值属性(如年龄、船票实付款),故选择决策树算法来实现。\n",
+ "2.在具体操作上发现了一些问题,比如一些属性值的缺失导致程序出现了一些bug,在调整了算法结构后基本解决。另外由于训练集数据量较大,所生成的不经修饰的决策树在测试集上效果不好,即产生了过拟合,经过实际验证,采用了预剪枝策略,实现了决策树的优化,并大大降低了决策时间成本。\n",
+ "3.最终结果如下方测试样例所示。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "outputs": [],
+ "source": [
+ "from detc_tree import *\n",
+ "dataset, features = create_dataset()\n",
+ "decision_tree = create_decision_tree(dataset, features)\n",
+ "# 打印生成的决策树\n",
+ "print(\"生成决策树:\",decision_tree)\n",
+ "# 对新样本进行分类测试\n",
+ "with open(\"test.csv\",mode='r') as f:\n",
+ " file=f.readlines()\n",
+ " for i,l in enumerate(file):\n",
+ " file[i] = l.split(',')\n",
+ " file[i].pop(2)\n",
+ " file[i].pop(0)\n",
+ " for j,c in enumerate(file[i]):\n",
+ " file[i][j] = c.split('\\n')[0]\n",
+ " if IsFloatNum(c):\n",
+ " file[i][j] = float(c)\n",
+ "index = 10\n",
+ "test_example = file[index]\n",
+ "features = file[0]\n",
+ "print(\"\\n\"+str(index)+'号人员是否存活:', bool(classify(decision_tree, features, test_example)))"
+ ],
+ "metadata": {
+ "collapsed": false,
+ "pycharm": {
+ "name": "#%%im\n",
+ "is_executing": true
+ }
+ }
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
"\n",
"## 总结\n",
- "总结任务实现过程所取得的心得等。"
- ]
+ "可见本程序能较好地完成是否存活的推断任务,并具有良好的准确率,但仍有进一步优化地空间。"
+ ],
+ "metadata": {
+ "collapsed": false
+ }
}
],
"metadata": {
diff --git a/report_02_Titanic/treePlotter.py b/report_02_Titanic/treePlotter.py
new file mode 100644
index 0000000000000000000000000000000000000000..dd4fca85d128914a19d36e89159b2232dea90ad8
--- /dev/null
+++ b/report_02_Titanic/treePlotter.py
@@ -0,0 +1,89 @@
+"""
+Created on Oct 14, 2010
+
+@author: Peter Harrington
+"""
+
+import matplotlib.pyplot as plt
+
+decisionNode = dict(boxstyle="sawtooth", fc="0.8")
+leafNode = dict(boxstyle="round4", fc="0.8")
+arrow_args = dict(arrowstyle="<-")
+
+
+def getNumLeafs(myTree):
+ numLeafs = 0
+ firstStr = list(myTree.keys())[0]
+ secondDict = myTree[firstStr]
+ for key in secondDict.keys():
+ if type(secondDict[
+ key]).__name__ == 'dict': # test to see if the nodes are dictonaires, if not they are leaf nodes
+ numLeafs += getNumLeafs(secondDict[key])
+ else:
+ numLeafs += 1
+ return numLeafs
+
+
+def getTreeDepth(myTree):
+ maxDepth = 0
+ firstStr = list(myTree.keys())[0]
+ secondDict = myTree[firstStr]
+ for key in secondDict.keys():
+ if type(secondDict[
+ key]).__name__ == 'dict': # test to see if the nodes are dictonaires, if not they are leaf nodes
+ thisDepth = 1 + getTreeDepth(secondDict[key])
+ else:
+ thisDepth = 1
+ if thisDepth > maxDepth: maxDepth = thisDepth
+ return maxDepth
+
+
+def plotNode(nodeTxt, centerPt, parentPt, nodeType):
+ createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords='axes fraction',
+ xytext=centerPt, textcoords='axes fraction',
+ va="center", ha="center", bbox=nodeType, arrowprops=arrow_args)
+
+
+def plotMidText(cntrPt, parentPt, txtString):
+ xMid = (parentPt[0] - cntrPt[0]) / 2.0 + cntrPt[0]
+ yMid = (parentPt[1] - cntrPt[1]) / 2.0 + cntrPt[1]
+ createPlot.ax1.text(xMid, yMid, txtString, va="center", ha="center", rotation=30)
+
+
+def plotTree(myTree, parentPt, nodeTxt): # if the first key tells you what feat was split on
+ numLeafs = getNumLeafs(myTree) # this determines the x width of this tree
+ depth = getTreeDepth(myTree)
+ firstStr = list(myTree.keys())[0] # the text label for this node should be this
+ cntrPt = (plotTree.xOff + (1.0 + float(numLeafs)) / 2.0 / plotTree.totalW, plotTree.yOff)
+ plotMidText(cntrPt, parentPt, nodeTxt)
+ plotNode(firstStr, cntrPt, parentPt, decisionNode)
+ secondDict = myTree[firstStr]
+ plotTree.yOff = plotTree.yOff - 1.0 / plotTree.totalD
+ for key in secondDict.keys():
+ if type(secondDict[
+ key]).__name__ == 'dict': # test to see if the nodes are dictonaires, if not they are leaf nodes
+ plotTree(secondDict[key], cntrPt, str(key)) # recursion
+ else: # it's a leaf node print the leaf node
+ plotTree.xOff = plotTree.xOff + 1.0 / plotTree.totalW
+ plotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode)
+ plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key))
+ plotTree.yOff = plotTree.yOff + 1.0 / plotTree.totalD
+
+
+# if you do get a dictonary you know it's a tree, and the first element will be another dict
+
+def createPlot(inTree, name):
+ fig = plt.figure(1, facecolor='white')
+ fig.clf()
+ axprops = dict(xticks=[], yticks=[])
+ createPlot.ax1 = plt.subplot(111, frameon=False, **axprops) # no ticks
+ # createPlot.ax1 = plt.subplot(111, frameon=False) #ticks for demo puropses
+ plotTree.totalW = float(getNumLeafs(inTree))
+ plotTree.totalD = float(getTreeDepth(inTree))
+ plotTree.xOff = -0.5 / plotTree.totalW
+ plotTree.yOff = 1.0
+ plotTree(inTree, (0.5, 1.0), '')
+ # plt.savefig('13数据分布情况')
+ plt.savefig(str(name))
+ plt.show()
+
diff --git a/report_02_Titanic/v2.0.py b/report_02_Titanic/v2.0.py
new file mode 100644
index 0000000000000000000000000000000000000000..ae4fd1485d783e1651cc9d2a22a228f64d6aeda4
--- /dev/null
+++ b/report_02_Titanic/v2.0.py
@@ -0,0 +1,440 @@
+# 决策树生成算法
+
+import pandas as pd
+from math import log2
+import numpy
+import treePlotter
+
+def read_excel_df(url: "绝对路径") -> "DataFrame类型":
+ """
+ :param url: url需要使用绝对路径, 相对路径会报错
+ :return: 返回Pandas.DataFrame类型数据
+ """
+ return pd.read_excel(url)
+ # pass
+
+
+# 计算信息熵
+def calculate_entropy(data: "DateFrame格式") -> "返回浮点数 entropy":
+ """
+ 计算公式 entropy = -pi * log2(pi) i为分类的个数
+ 1. 获取非分类属性的属性值以及个数
+ 2. 按照计算公式计算信息熵
+ :return: entropy(float)
+ """
+
+ # 获取列
+ columns_items = data[data.columns[-1]]
+
+ # 获取取值以及取值的个数
+ labels = {}
+
+ for item, cnt in columns_items.value_counts().items(): # items 返回(value, count), 值, 个数
+ labels[item] = cnt
+
+ # 所有数据的行数
+ rows_count = len(columns_items)
+
+ # 计算信息熵
+ entropy = 0
+ for key in labels:
+ p = labels[key] / rows_count
+ entropy -= p * log2(p)
+
+ return entropy
+ pass
+
+
+# 划分数据集
+def split_data(data: "DateFrame类型, 按照连续值划分", feature: "列名", feature_value: "列属性的取值") -> "按照属性的某个取值划分后的子集":
+ """
+ 根据信息 featrure属性 和 属性的取值 划分数据集
+ :param data: 类型 DataFrame
+ :param feature: DataFrame表的列头
+ :param feature_value: 每一列属性的取值
+ :return: 返回 属性列 feature 按照固定属性值 feature_value 的数据子集
+ 去掉分类时的属性
+ """
+ sub_data = []
+ n = len(data)
+
+ # 遍历数据的每一行
+ for i in range(n):
+ if data.iloc[[i], :][feature].values[0] == feature_value:
+ temp = data.loc[i] # 取改行数据
+ sub_data.append(list(temp))
+
+ sub_data_p = pd.DataFrame(sub_data, columns=list(data.columns))
+
+ # 去掉已经被划分的属性
+ del sub_data_p[feature]
+ return sub_data_p
+
+
+# 处理连续值得信息增益划分
+def settle_continuous_data(data: "DateFrame数据,不对连续值处理") -> "返回保存连续值属性以及最优划分得字典":
+ """
+ 处理过程
+ 1. 首先对每一列进行处理,规定连续值得数据类型是 int, float类型
+ 2. 保存每一列得名字到列表, 方便对后面得方便对后面得表头进行修改
+ 3. 对数值列进行处理, 取出这一列得数值,取 两两中值 对每一个中值进行信息增益的计算, 信息增益最大的
+ · 则是最优的连续值属性划分
+ 4.处理细节
+ 对每一个中间值进行信息增益的求解
+ :param data: DataFrame类型的数据
+ :return: 返回处理的data, 最优的连续值属性划分添加到表头
+ """
+ # 连续值字典保存
+ continuous_number_dict = {}
+
+ # 按照列进行遍历
+ cols_count = len(data.columns)
+
+ # 保存列的名字进行设置
+ cols_names = list(data.columns)
+
+ for i in range(cols_count - 1): # 去掉最后一列类属性
+ if isinstance(data.iloc[0, i], (numpy.int64, numpy.float64)): # 如果取值是数值类型,则按照连续值进行处理
+ col_data = list(data.iloc[:, i]) # 取出连续值列, 使用集合进行去重操作
+ col_data.sort() # 从小到达的顺序进行排序
+ col_len = len(col_data)
+
+ print("col_data:", col_data)
+
+ # 计算中间值,保存到average
+ average = []
+ for j in range(col_len - 1):
+ average.append((col_data[j] + col_data[j + 1]) / 2)
+
+ # 最优的连续值
+ best_feature = 0
+
+ # 最优信息增益
+ best_info_gain = 0.0
+
+ # 未进行属性划分时的信息熵
+ base_entropy = calculate_entropy(data)
+
+ print("average: ", average)
+
+ # 对每一个中间值进行处理
+ for num in average:
+ # 中间值属性划分的信息熵
+ feature_entropy = 0.0
+
+ # 深拷贝一个该数据集, 对新的数据集的连续值,按照中间值,划分为 "大", "小", 原数据集还需要多次使用
+ new_data = data.copy(deep=True)
+ for j in range(len(data.iloc[:, i])):
+ if data.iloc[j, i] >= num:
+ new_data.iloc[j, i] = ">=" + str(num)
+ else:
+ new_data.iloc[j, i] = "<" + str(num)
+
+ # 2. 计算中间值划分后新数据集得信息增益
+
+ # 取连续值得列属性名
+ name_feature = new_data.columns[i]
+ # 属性得取值进行去重, 为了按照属性进行子数据集的划分
+ unique_value = set(new_data[name_feature])
+
+ # 按照每一个属性值划分子集, 计算信息增益
+ for value in unique_value:
+ sub_data = split_data(new_data, name_feature, value)
+ rate = len(sub_data) / len(new_data)
+ feature_entropy += rate * calculate_entropy(sub_data)
+ info_gain = base_entropy - feature_entropy
+
+ # 保存最优的中间值为属性值
+ if info_gain > best_info_gain:
+ best_info_gain = info_gain
+ best_feature = num
+
+ # 显示最优的连续属性值划分
+ # print("连续值处理: best_info_gain: ", info_gain)
+ # print("连续值处理: Best feature ", best_feature)
+ # # 保存属性列的名称
+ # cols_names[i] = best_feature
+
+ # 保存属性到字典
+ continuous_number_dict[cols_names[i]] = best_feature
+ print("保存到字典的属性: ", continuous_number_dict)
+ # 返回连续值矩阵
+ return continuous_number_dict
+ pass
+
+
+# 选择最优的属性进行划分
+def choose_best_feature_to_split(data: "原始的DataFrame数据集") -> "返回":
+ """
+ 选择最优的属性
+ 1. 对连续值进行处理, 深拷贝一个数据集,对深拷贝的数据集,按照连续属性的最优划分(表头), 将属性划分为"大" 或 "小"
+ 2. 对每一列计算其信息熵和信息增益, 选择最大的信息增益的属性作为最优的属性划分
+ :param new_data: 已经进行最优连续值划分的数据集
+ :return: 返回最优的属性名, 用于决策树的建立
+ """
+ # 1. 首先对连续值进行处理
+ # 深拷贝一个数组
+
+ # 获取连续值
+ continuous_number_dict = settle_continuous_data(data)
+
+ # 深拷贝, 不破坏连续值
+ copy_data = data.copy(deep=True)
+ data_columns = list(copy_data.columns)
+
+ # 保存连续值的列表
+ num_feature_list = []
+
+ # 连续值的列保存到列表中
+ for item in continuous_number_dict.keys():
+ num_feature_list.append(item)
+
+ # 对每一个连续值列, 修改属性的取值为">" 或 "<"
+ for name in num_feature_list:
+ for i in range(len(copy_data)):
+
+ # 防止去掉属性后,因为Pandas.loc[i, name] name没有该列出错
+ if name in data_columns:
+ if copy_data.loc[i, name] >= continuous_number_dict[name]:
+ copy_data.loc[i, name] = ">=" + str(continuous_number_dict[name])
+ else:
+ copy_data.loc[i, name] = "<" + str(continuous_number_dict[name])
+
+ # 显示处理后的数据集
+ # print("最优属性选择后的数据集: ", copy_data)
+
+ # 2. 对连续属性值处理后的数据集进行最优属性选择
+ # 取出数据集列
+ name_feature = copy_data.columns
+
+ # 计算未进行属性划分时数据集的信息熵
+ base_entropy = calculate_entropy(copy_data)
+
+ # 初始化 信息增益 和 最优划分属性
+ best_info_gain = 0.0
+ best_feature = 0
+
+ # 对数据集的每一列进行处理
+ for feature in name_feature[0:-1]:
+ unique_value = set(copy_data[feature]) # 取出去重后的属性取值
+
+ # 每一个属性的信息熵初始化
+ feature_entropy = 0.0
+
+ # 对每一个属性进行属性划分, 计算其信息增益
+ for value in unique_value:
+ sub_data = split_data(copy_data, feature, value)
+ rate = len(sub_data) / len(copy_data)
+ feature_entropy += rate * calculate_entropy(sub_data)
+ info_gain = base_entropy - feature_entropy
+
+ # 显示信息增益
+ print("最优属性划分: 信息增益: column: ", feature, " 信息增益: ", info_gain)
+
+ # 保存最优的属性划分
+ if info_gain > best_info_gain:
+ best_info_gain = info_gain
+ best_feature = feature
+
+ # 显示最优的属性划分
+ print("最优属性划分: 最优属: ", best_feature)
+ return best_feature
+ pass
+
+
+# 建立决策树
+def create_tree(data: "原始DateFrame数据集") -> "返回构造的Dict字典树":
+ """
+ 建立决策树
+ 1. 递归结束的条件:
+ 1). 只有类属性列,且类属性列的取值唯一, 取唯一的属性
+ 2). 只有类属性列,类属性的取值不唯一,取概率较大的类为该类的划分
+ 2. 对最优属性的连续值进行处理, 大于最优连续值取值为为 "大" 其余 取值为小
+ 2. 取最优的属性值,按照其属性的分类,划分子树,子树为字典取值,建立决策树
+ :param data: 原始的数据集,未经过连续值得处理
+ :return: 返回字典, 字典的键为 属性 值为属性或者划分类
+ """
+
+ # 1. 只有类属性时
+ # 取去掉分类的列, 分类是最后一列
+ data_columns = data.columns
+ columns_value = data[data_columns[-1]]
+
+ # 结束递归条件 只有一个类是结束划分
+ if len(columns_value.values) == columns_value.value_counts()[0]:
+ # print("len(columns_value.values): ", len(columns_value.values))
+ # print("columns_value.value_counts()[0]: ", columns_value.value_counts()[0])
+ # print("columns_value.values: ", columns_value.values)
+ # print("columns_value.value_counts(): ", columns_value.value_counts())
+ print("类的取值唯一,递归结束")
+ return columns_value.values[0]
+
+ # 递归结束的条件 只有一个属性的时候, 取分类数目较多的属性
+ if len(data_columns) == 1:
+ print("只有类那一列,类的取值不同")
+ class_dict = {}
+
+ # 取类取值最多的那一列
+ for item, value in columns_value.value_counts().items():
+ class_dict[item] = value
+
+ # 排序取最大
+ class_name = max(class_dict, key=lambda k: class_dict[k])
+ return class_name
+
+ # 2. 非只有类属性时
+
+ # 选择最优的属性划分
+ best_feature = choose_best_feature_to_split(data)
+
+ # 获取连续值
+ continuous_number_dict = settle_continuous_data(data)
+
+ if best_feature in continuous_number_dict.keys():
+ for i in range(len(data)):
+ if data.loc[i, best_feature] >= continuous_number_dict[best_feature]:
+ data.loc[i, best_feature] = ">=" + str(continuous_number_dict[best_feature])
+ else:
+ data.loc[i, best_feature] = "<" + str(continuous_number_dict[best_feature])
+
+ # 建立字典树,类型为{属性名: {分类 / 分支}}
+ tree = {best_feature: {}}
+
+ # 取最优属性列的值,递归划分数据集
+ unique_value = set(data[best_feature])
+ # 遍历每一个属性进行划分
+ for value in unique_value:
+ tree[best_feature][value] = create_tree(split_data(data, best_feature, value))
+
+ return tree
+ pass
+
+# 测试集中的数据离散化处理
+def discrete_data(decision_tree: "建立的生成树", dict_test: "测试例子字典") -> "返回离散化处理后数据集":
+ """
+ 对测试集中的连续值进行离散化处理
+ 1. 对于生成树, 第一层字典的值是属性列, 第二层是属性的取值
+ 2. 取出第一层属性作为属性列, 取出第二层的属性作为属性的取值
+ 3. 对测试集进行处理, 取出其中取值是数字(即连续性值)的属性列
+ 4. 如果连续值得属性列 是 这个子树的第一层键(根节点), 比较第二层属性列, 进行连续值离散化处理
+ 1). 大于属性值, 取>=0.2222
+ 2). 小于属性值, 取 <0.222
+ 5. 递归对下一层字典进行处理
+ :param decision_tree:
+ :param dict_test:
+ :return:
+ """
+ # 如果字典树的 属性列 取值不为None
+ if decision_tree.keys() is not None:
+ # 取根节点的列属性
+ root_column = list(decision_tree.keys())[0]
+
+ # 取字典值字典的序
+ second_dict = decision_tree[root_column]
+ # 取第二层字典键,即是属性的取值
+ decision_value = list(second_dict.keys())
+
+ consecutive_column = []
+ for key in dict_test.keys():
+ if isinstance(dict_test[key], (int, float)):
+ consecutive_column.append(key)
+
+ # 对连续进行处理
+ for conlumn_value in consecutive_column:
+ # 取测试集的key 与 跟节点的属性进行比较
+ if root_column == conlumn_value:
+ for value in decision_value:
+ # 对测试集字典中的熟悉离散化处理
+ if value[0: 2] == ">=":
+ edit_value = value.replace("=", "")
+ else:
+ edit_value = value
+ if isinstance(dict_test[root_column], float):
+ # 测试集的属性 >= 最佳连续值的时候, 修改测试集中的连续值为: ">=最优连续值"
+ if float(edit_value[1:]) <= float(dict_test[root_column]):
+ dict_test[root_column] = ">=" + str(float(edit_value[1:]))
+ # 测试集的属性 < 最佳连续值的时候, 修改测试集中的连续值为: "<最优连续值"
+ elif float(str(edit_value)[1:]) > dict_test[root_column]:
+ dict_test[root_column] = "<" + str(float(edit_value[1:]))
+
+ for key in second_dict.keys():
+ if key == dict_test[root_column]:
+ if isinstance(second_dict[key], dict):
+ discrete_data(second_dict[key], dict_test)
+ return dict_test
+ pass
+
+# 分类器预测
+def match_class(decision_tree: "建立的生成树", dict_test: "测试的数据集") -> "返回判断后的类别":
+ """
+ 根据生成的决策时进行预测
+ 1. 对于生成树, 第一层是属性列的值, 第二层是属性的取值
+ 2. 对于测试字典, 第一层是属性列, 第二层是属性列的取值
+ 3. 取生成树第一层属性列, 和第二层属性的取值, 属性值于测试集中对应列进行比较
+ 1). 相等的时候, 判断属性的下一层的值是不是字典, 如果是字典, 则递归子树, 将值保存到class_label中作为结果
+ 2). 下一层不是字典, 而是取值的时候, 赋值到class_label
+ 4. 返回结果集为class_label
+
+ :param decision_tree: 建立的决策树
+ :param dict_test: : 测试集字典
+ :return: 返回生成树的分类
+ """
+ root_column = list(decision_tree.keys())[0]
+ second_dict = decision_tree[root_column] # 获取字典树的取值, 第二个字典即属性取值
+ for key in second_dict.keys():
+ if key == dict_test[root_column]:
+ if type(second_dict[key]).__name__ == "dict": # 继续递归
+ class_label = match_class(second_dict[key], dict_test)
+ else:
+ class_label = second_dict[key]
+ return class_label
+ pass
+
+# 对生成树进行预测
+def predict(decision_tree: "建立的决策树", dict_test: "测试集的字典类型") -> "返回是 好瓜 或者 坏瓜":
+ discrete_dict_test = discrete_data(decision_tree, dict_test)
+ predict_result = match_class(decision_tree, discrete_dict_test)
+ return "好瓜" if predict_result == "Yes" else "坏瓜"
+ pass
+
+
+if __name__ == "__main__":
+
+ # 作业数据集
+ url_1 = r"作业数据集.xlsx"
+
+ url_2 = r"作业数据集English.xlsx"
+
+ # 西瓜数据集
+ url_3 = r"西瓜数据集.xlsx"
+
+ url_4 = r"西瓜数据集English.xlsx"
+
+ url_5 = r"train.xlsx"
+
+ # 建立生成树
+ data = read_excel_df(url_5) # 读取数据
+ decision_tree = create_tree(data) # 根据DataFrame数据建立字典树
+ print("建立的决策树字典: ", decision_tree) # 显示建立的字典树
+ # 绘制生成树
+ # treePlotter.createPlot(decision_tree, "WaterMellon_color") # 绘制图
+
+ # dict_test = {
+ # "SeZe": "Wuhei",
+ # "GenDi": "QuanSuo",
+ # "QiaoSheng": "ZhuoXiang",
+ # "WenLi": "ShaoHu",
+ # "QiBu": "ShaoAo",
+ # "ChuGan": "RuanNian",
+ # "MiDu": 0.2,
+ # "HanTangLv": 0.15
+ #
+ # }
+ #
+ # # 对预测值进行处理
+ # decision_tree = {'WenLi': {'ShaoHu': {'ChuGan': {'YingHua': 'No', 'RuanNian': 'Yes'}},
+ # 'QingXi': {'MiDu': {'<0.3815': 'No', '>=0.3815': 'Yes'}}, 'MoHu': 'No'}}
+ #
+ # predict_result = predict(decision_tree, dict_test)
+ # print("预测的结果为: ", predict_result)
+
diff --git a/report_03_Fashion/.idea/.gitignore b/report_03_Fashion/.idea/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..26d33521af10bcc7fd8cea344038eaaeb78d0ef5
--- /dev/null
+++ b/report_03_Fashion/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/report_03_Fashion/.idea/inspectionProfiles/profiles_settings.xml b/report_03_Fashion/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..105ce2da2d6447d11dfe32bfb846c3d5b199fc99
--- /dev/null
+++ b/report_03_Fashion/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/report_03_Fashion/.idea/misc.xml b/report_03_Fashion/.idea/misc.xml
new file mode 100644
index 0000000000000000000000000000000000000000..271af782139647a9b940f507e9719115cfdc988a
--- /dev/null
+++ b/report_03_Fashion/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/report_03_Fashion/.idea/modules.xml b/report_03_Fashion/.idea/modules.xml
new file mode 100644
index 0000000000000000000000000000000000000000..10a2580ac71db4a58222c9892fdca2d3901bf14b
--- /dev/null
+++ b/report_03_Fashion/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/report_03_Fashion/.idea/report_03_Fashion.iml b/report_03_Fashion/.idea/report_03_Fashion.iml
new file mode 100644
index 0000000000000000000000000000000000000000..c0c62529df586ceca4dbbc846de3b252fd332a4d
--- /dev/null
+++ b/report_03_Fashion/.idea/report_03_Fashion.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/report_03_Fashion/CNN_model.py b/report_03_Fashion/CNN_model.py
new file mode 100644
index 0000000000000000000000000000000000000000..6e486624be7b82049f7de4c1985c2ac3fe5854e9
--- /dev/null
+++ b/report_03_Fashion/CNN_model.py
@@ -0,0 +1,136 @@
+import torch
+from torch import nn
+from torch.nn import functional as F
+
+class Conv_bn_pool(nn.Module):
+ def __init__(self,in_channels,out_channels,kernel_size,stride,padding,eps=1e-5,momentum=1,affine=True,
+ track_running_states=True,pool='',pool_size=None,pool_stride=None,pool_padding=(0,0)):
+ super(Conv_bn_pool,self).__init__()
+ self.conv = nn.Conv2d(in_channels=in_channels,out_channels=out_channels,kernel_size=kernel_size,
+ stride=stride,padding=padding)
+ nn.init.orthogonal_(self.conv.weight)
+ self.pool = nn.Sequential()
+ self.bn = nn.Sequential()
+ self.act = nn.Sequential()
+ if pool=='max':
+ self.pool = nn.MaxPool2d(pool_size,stride=pool_stride,padding=pool_padding)
+ self.bn = nn.BatchNorm2d(out_channels, eps=eps, momentum=momentum, affine=affine,
+ track_running_stats=track_running_states)
+ self.act = nn.ReLU()
+ elif pool=='avg':
+ self.pool = nn.AvgPool2d(pool_size,stride=pool_stride,padding=pool_padding)
+ self.bn = nn.BatchNorm2d(out_channels, eps=eps, momentum=momentum, affine=affine,
+ track_running_stats=track_running_states)
+ self.act = nn.ReLU()
+
+
+ def forward(self, inp):
+ output = self.conv(inp)
+ output = self.bn(output)
+ output = self.act(output)
+ output = self.pool(output)
+
+ # print(output.shape)
+ return output
+
+
+class Conv_bn_dynamic_apool(nn.Module):
+ def __init__(self,in_channels,out_channels,kernel_size,stride,padding,eps=1e-5,momentum=1,affine=True,
+ track_running_states=True,pool_padding=(1,1)):
+ super(Conv_bn_dynamic_apool, self).__init__()
+ self.conv = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size,
+ stride=stride, padding=padding)
+ nn.init.orthogonal_(self.conv.weight)
+ self.bn = nn.BatchNorm2d(out_channels, eps=eps, momentum=momentum, affine=affine,
+ track_running_stats=track_running_states)
+ self.act = nn.ReLU()
+ self.pooling = nn.MaxPool2d((2,2),stride=(2,2),padding=pool_padding)
+ self.gapool = nn.AdaptiveAvgPool2d((1,1))
+
+ def forward(self, inp):
+ output = self.conv(inp)
+ output = self.bn(output)
+ output = self.act(output)
+ # print(output.shape)
+ output = self.pooling(output)
+ output = self.gapool(output)
+ output = output.squeeze()
+ if len(output.shape) < 4:
+ output = output.unsqueeze(0)
+ # print(output.shape)
+ return output
+class Cnn_model(nn.Module):
+
+ def __init__(self,n_classes):
+ super(Cnn_model,self).__init__()
+ inp = torch.zeros([1,28,28])
+ inp=inp.unsqueeze(0)
+ self.layer1 = Conv_bn_pool(in_channels=inp.size(1), out_channels=48, kernel_size=(3, 3), stride=(1, 1),
+ padding=(0, 1), pool='max', pool_size=(2,2), pool_stride=(2,2),pool_padding=(1,1))
+ self.layer2 = Conv_bn_pool(in_channels=48, out_channels=96, kernel_size=(3, 3), stride=(1, 1),
+ padding=(0, 1), pool='max', pool_size=(2,2), pool_stride=(2,2),pool_padding=(1,1))
+ self.layer3_1 = Conv_bn_pool(in_channels=96, out_channels=128, kernel_size=(3, 3), stride=(1, 1),
+ padding=(1, 1))
+ self.layer3_2 = Conv_bn_pool(in_channels=128, out_channels=256, kernel_size=(3, 3), stride=(1, 1),
+ padding=(1, 0), pool='max', pool_size=(2,2), pool_stride=(2,2),pool_padding=(1,1))
+ self.layer4 = Conv_bn_dynamic_apool(in_channels=256, out_channels=256, kernel_size=(3, 3), stride=(1, 2),
+ padding=(1, 1))
+ self.layer5 = nn.Linear(256,n_classes,bias=True)
+ nn.init.orthogonal_(self.layer5.weight)
+ # self.layer6 = nn.Linear(128,n_classes,bias=True)
+ # nn.init.orthogonal_(self.layer6.weight)
+ # self.layer7 = Conv_bn_dynamic_apool(in_channels=256, out_channels=256, kernel_size=(1, 1), stride=(1, 1),
+ # padding=(0, 0))
+ # self.layer8 = Conv_bn_pool(in_channels=256, out_channels=512, kernel_size=(1, 1), stride=(1, 1),
+ # padding=(0, 0))
+ # #归一化
+ self.dropout = nn.Dropout(0.1)
+ self.act = nn.ReLU()
+ # # self.layer8= Conv_bn_pool(in_channels=1024, out_channels=1024, kernel_size=(1, 1), stride=(1, 1),
+ # # padding=(0, 0))
+ # self.layer9 = Conv_bn_pool(in_channels=512, out_channels=n_classes, kernel_size=(1, 1), stride=(1, 1),
+ # padding=(0, 0))
+ # self.criterion = nn.CrossEntropyLoss()
+
+ def forward(self, inp, tgt, batch_size, n_classes):
+
+ x = self.layer1(inp)
+ x = self.layer2(x)
+ x = self.layer3_1(x)
+ x = self.layer3_2(x)
+ # # 归一化
+ x = F.normalize(x,p=2,dim=1,eps=1e-12)
+
+ x = self.layer4(x)
+ # print(x.shape)
+ x = self.layer5(x)
+ # x = self.dropout(x)
+ # x = self.act(x)
+ # x = self.dropout(x)
+
+ # x = self.layer6(x)
+ # print(x.shape)
+ logits = x.view(batch_size,n_classes)
+
+ # loss = self.criterion(logits,tgt.long())
+ predict = torch.argmax(F.softmax(logits,dim=1),dim=1,keepdim=False)
+ correct = 0
+ for i in range(batch_size):
+ if predict[i].int() == tgt[i].int():
+ correct += 1
+ # elif batch_size == 1:
+ # print(int(tgt[i].item()),"->",predict[i].item())
+ # recognize_wrong = int(tgt[i].item())
+ # recognize_wrong_to = predict[i].item()
+
+ acc = float(correct)/batch_size
+ return logits, acc
+
+
+
+if __name__ == "__main__":
+ model = Cnn_model(10)
+ data = torch.rand([8,1,28,28])
+ label = torch.rand([8])
+ logits,acc = model(data,label,8,10)
+
diff --git a/report_03_Fashion/__pycache__/CNN_model.cpython-38.pyc b/report_03_Fashion/__pycache__/CNN_model.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..f2de3bd2657d51890c9d166324f8c649d297fd71
Binary files /dev/null and b/report_03_Fashion/__pycache__/CNN_model.cpython-38.pyc differ
diff --git a/report_03_Fashion/__pycache__/load_data.cpython-38.pyc b/report_03_Fashion/__pycache__/load_data.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..b8f9dc2d88785c7b05548c83f8e59880639ec513
Binary files /dev/null and b/report_03_Fashion/__pycache__/load_data.cpython-38.pyc differ
diff --git a/report_03_Fashion/load_data.py b/report_03_Fashion/load_data.py
new file mode 100644
index 0000000000000000000000000000000000000000..329edcd9f2158dc0ec756e2d185a2a663f2b5044
--- /dev/null
+++ b/report_03_Fashion/load_data.py
@@ -0,0 +1,19 @@
+import os
+import struct
+import numpy as np
+
+
+def load_mnist(images_path = 't10k-labels.npy',labels_path = 't10k-images.npy'):
+ """Load MNIST data from `path`"""
+
+ label = np.load(labels_path)
+ image = np.load(images_path)
+
+ return image, label
+
+if __name__ == "__main__":
+ data = load_mnist()
+ image = data[0]
+ label = data[1]
+ print(image.shape)
+ print(label.shape)
\ No newline at end of file
diff --git a/report_03_Fashion/main.py b/report_03_Fashion/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..3e3c71526d7467444699ba2907c3595befbb49b8
--- /dev/null
+++ b/report_03_Fashion/main.py
@@ -0,0 +1,130 @@
+import numpy as np
+import torch
+import torch.nn as nn
+from torch import optim
+import load_data
+import CNN_model
+
+def train(iden_class,epochs,bsz,lr,weight_decay,images_path,labels_path,save_model):
+
+ device = torch.device('cuda')
+
+ data = load_data.load_mnist(images_path,labels_path)
+
+ image = torch.tensor(data[0]).to(device).float()
+ label = torch.tensor(data[1]).to(device).float()
+
+ image = image.reshape(-1,28,28).unsqueeze(1)
+
+ rand_num = torch.randperm(image.size(0)//bsz*bsz)
+
+ rand_num = rand_num.reshape(-1,bsz)
+
+ model = CNN_model.Cnn_model(iden_class).to(device)
+ # torch.save(model, "model.bin")
+
+ optimizer = optim.Adam(model.parameters(),lr=lr,betas=(0.9,0.999),eps=1e-8,weight_decay=weight_decay)
+ # scheduler = lr_scheduler.ExponentialLR(optimizer, gamma=0.9)
+ criterion = nn.CrossEntropyLoss()
+
+ train_loss_p = []
+ train_acc_p = []
+ acc_highest = 0
+
+ for epoch in range(epochs):
+ loss = 0
+ acc=0
+ acc_high=0
+ print("epoch:",epoch + 1,"/",epochs)
+ model.train()
+ for batch_id in range(image.size(0)//bsz):
+ print("epoch",epoch + 1," batch:",batch_id + 1,"/",image.size(0)//bsz,end=" ")
+ batch_data = [image[rand_num[batch_id]],label[rand_num[batch_id]]]
+ batch_image = batch_data[0]
+ batch_label = batch_data[1]
+ logits, acc_batch = model(batch_image, batch_label, bsz, iden_class)
+
+ loss_batch = criterion(logits, batch_label.long()).to(device)
+ print("loss: ", loss_batch.item(),end=" ")
+ print("acc: {:.0%}".format(acc_batch))
+ train_loss_p.append(loss_batch.item())
+ train_acc_p.append(acc_batch)
+
+ acc_high = acc_batch if acc_batch > acc_high else acc_high
+
+ loss += loss_batch.item()
+ acc += acc_batch
+
+ optimizer.zero_grad()
+ loss_batch.backward()
+ optimizer.step()
+
+ # scheduler.step(epoch)
+
+ loss /= image.size(0)//bsz
+ acc /= image.size(0)//bsz
+ if acc >= acc_highest:
+ acc_highest = acc
+ if save_model:
+ torch.save(model, save_model)
+ state_dic = {
+ "state": model.state_dict(),
+ "epoch": epoch
+ }
+
+ print("epoch",epoch + 1,": train_loss:",loss," train_acc:{:.0%}".format(acc)," train_acc_high:",acc_high)
+
+ return acc_highest
+
+def test(epoch,iden_class,model_path,images_path,labels_path,bsz=1):
+
+ device = torch.device('cuda')
+
+ data = load_data.load_mnist(images_path,labels_path)
+
+ image = torch.tensor(data[0]).to(device).float()
+ label = torch.tensor(data[1]).to(device).float()
+
+ image = image.reshape(-1,28,28).unsqueeze(1)
+
+ model = torch.load(model_path)
+
+ # scheduler = lr_scheduler.ExponentialLR(optimizer, gamma=0.9)
+ criterion = nn.CrossEntropyLoss()
+
+ test_loss_p = []
+ test_acc_p = []
+
+ acc = 0
+ loss = 0
+ model.eval()
+ for batch_id in range(image.size(0)//bsz):
+ batch_data = [image[batch_id].unsqueeze(0),label[batch_id].unsqueeze(0)]
+ batch_image = batch_data[0]
+ batch_label = batch_data[1]
+ logits, acc_batch = model(batch_image, batch_label, bsz, iden_class)
+
+ loss_batch = criterion(logits, batch_label.long()).to(device)
+ test_loss_p.append(loss_batch.item())
+ test_acc_p.append(acc_batch)
+
+
+ loss += loss_batch.item()
+ acc += acc_batch
+
+
+ # scheduler.step(epoch)
+
+ loss /= image.size(0)//bsz
+ acc /= image.size(0)//bsz
+
+ print("epoch",epoch,": test_loss:",loss," test_acc:{:.0%}".format(acc))
+
+
+def main(iden_class,epochs,bsz,lr,weight_decay,train_images_path,train_labels_path,test_images_path,test_labels_path,save_model):
+ acc = train(iden_class,epochs,bsz,lr,weight_decay,train_images_path,train_labels_path,save_model)
+ test(epochs,iden_class,save_model,test_images_path,test_labels_path)
+
+if __name__ == "__main__":
+ main(10,0,100,1e-5,5e-5,'t10k-images.npy','t10k-labels.npy','train-images.npy','train-labels.npy','model.bin')
+ test(10,10,'model.bin','train-images.npy','train-labels.npy')
\ No newline at end of file
diff --git a/report_03_Fashion/model.bin b/report_03_Fashion/model.bin
new file mode 100644
index 0000000000000000000000000000000000000000..4aef72441b6827f3f74853a92d2fd5406a903e88
Binary files /dev/null and b/report_03_Fashion/model.bin differ
diff --git a/report_03_Fashion/report_template.ipynb b/report_03_Fashion/report_template.ipynb
index 21eb1ce711b96fd98ffe44be0b1697fee849508a..0c323f3a6b112a133b768d4484c2d086928fe4f1 100644
--- a/report_03_Fashion/report_template.ipynb
+++ b/report_03_Fashion/report_template.ipynb
@@ -4,32 +4,48 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Report - 报告题目\n",
+ "# Report - Fashion\n",
"\n",
- "* 姓名\n",
- "* 学号\n",
+ "* 法品哲\n",
+ "* 2022265233\n",
"\n",
"\n",
"## 任务简介\n",
"\n",
- "这里简述一下任务是什么;数据的格式,包含了什么数据;最终的目标是什么\n",
+ "基于给定单通道服饰图片,判断图片中服饰类型。\n",
+ "类似于mnist手写数字识别的图像分类任务,甚至连数据组织格式、数据维度、图片尺寸都基本一致。\n",
"\n",
"## 解决途径\n",
"\n",
- "主要包括:\n",
- "1. 问题的思考,整体的思路\n",
- "2. 选用的方法,以及为何选用这些方法\n",
- "3. 实现过程遇到的问题,以及如何解决的\n",
- "4. 最终的结果,实验分析\n",
- "\n",
- "要求:\n",
- "1. 数据的可视化\n",
- "2. 程序,以及各个部分的解释、说明\n",
- "3. 结果的可视化,精度等的分析\n",
- "\n",
- "## 总结\n",
- "总结任务实现过程所取得的心得等。"
+ "1.由于其和mnist任务的相似性,应用于后者的算法基本可以很好地完成该任务,但考虑到手写数字识别的任务难度相较于本项目偏低,所以考虑使用更深更大尺寸的卷积神经网络来完成模型搭建\n",
+ "2.本项目较为简单,基本没有遇到太大问题,实现效果也比较好\n",
+ "3.10个epoch训练后,模型验证结果如下。"
]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "outputs": [],
+ "source": [
+ "import main\n",
+ "main.test(10,10,'model.bin','train-images.npy','train-labels.npy')"
+ ],
+ "metadata": {
+ "collapsed": false,
+ "pycharm": {
+ "is_executing": true
+ }
+ }
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "## 总结\n",
+ "可见CNN模型较为圆满地完成了本次任务"
+ ],
+ "metadata": {
+ "collapsed": false
+ }
}
],
"metadata": {
diff --git a/report_05_Jigsaw-Puzzle/.idea/.gitignore b/report_05_Jigsaw-Puzzle/.idea/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..045b268a4c2d0ac2c00d782d1b0f490155882228
--- /dev/null
+++ b/report_05_Jigsaw-Puzzle/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+*.csv
+*.xlsx
+*.npy
+*.zip
+*.jpg
\ No newline at end of file
diff --git a/report_05_Jigsaw-Puzzle/.idea/inspectionProfiles/profiles_settings.xml b/report_05_Jigsaw-Puzzle/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000000000000000000000000000000000000..105ce2da2d6447d11dfe32bfb846c3d5b199fc99
--- /dev/null
+++ b/report_05_Jigsaw-Puzzle/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/report_05_Jigsaw-Puzzle/.idea/misc.xml b/report_05_Jigsaw-Puzzle/.idea/misc.xml
new file mode 100644
index 0000000000000000000000000000000000000000..0d47592fedc8b7838ac2b3ff31a3a4e7e2d2e95a
--- /dev/null
+++ b/report_05_Jigsaw-Puzzle/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/report_05_Jigsaw-Puzzle/.idea/modules.xml b/report_05_Jigsaw-Puzzle/.idea/modules.xml
new file mode 100644
index 0000000000000000000000000000000000000000..6588831574e0f1bb0514fb0765257b2e5be95111
--- /dev/null
+++ b/report_05_Jigsaw-Puzzle/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/report_05_Jigsaw-Puzzle/.idea/report_05_Jigsaw-Puzzle.iml b/report_05_Jigsaw-Puzzle/.idea/report_05_Jigsaw-Puzzle.iml
new file mode 100644
index 0000000000000000000000000000000000000000..dee440581ed60fd4726321aaeeac56c4b9cd0482
--- /dev/null
+++ b/report_05_Jigsaw-Puzzle/.idea/report_05_Jigsaw-Puzzle.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/report_05_Jigsaw-Puzzle/CNN_model.py b/report_05_Jigsaw-Puzzle/CNN_model.py
new file mode 100644
index 0000000000000000000000000000000000000000..e8021bcf6d852054ad07e0e3c5ff609e956b996c
--- /dev/null
+++ b/report_05_Jigsaw-Puzzle/CNN_model.py
@@ -0,0 +1,181 @@
+import torch
+from torch import nn
+from torch.nn import functional as F
+import PatchSampleAttention
+
+class Conv_bn_pool(nn.Module):
+ def __init__(self,in_channels,out_channels,kernel_size,stride,padding,eps=1e-5,momentum=1,affine=True,
+ track_running_states=True,pool='',pool_size=None,pool_stride=None,pool_padding=(0,0)):
+ super(Conv_bn_pool,self).__init__()
+ self.conv = nn.Conv2d(in_channels=in_channels,out_channels=out_channels,kernel_size=kernel_size,
+ stride=stride,padding=padding)
+ nn.init.orthogonal_(self.conv.weight)
+ self.pool = nn.Sequential()
+ self.bn = nn.Sequential()
+ self.act = nn.Sequential()
+ if pool=='max':
+ self.pool = nn.MaxPool2d(pool_size,stride=pool_stride,padding=pool_padding)
+ self.bn = nn.BatchNorm2d(out_channels, eps=eps, momentum=momentum, affine=affine,
+ track_running_stats=track_running_states)
+ self.act = nn.ReLU()
+ elif pool=='avg':
+ self.pool = nn.AvgPool2d(pool_size,stride=pool_stride,padding=pool_padding)
+ self.bn = nn.BatchNorm2d(out_channels, eps=eps, momentum=momentum, affine=affine,
+ track_running_stats=track_running_states)
+ self.act = nn.ReLU()
+
+
+ def forward(self, inp):
+ output = self.conv(inp)
+ output = self.bn(output)
+ output = self.act(output)
+ output = self.pool(output)
+
+ # print(output.shape)
+ return output
+
+
+class Conv_bn_dynamic_apool(nn.Module):
+ def __init__(self,in_channels,out_channels,kernel_size,stride,padding,eps=1e-5,momentum=1,affine=True,
+ track_running_states=True,pool_padding=(1,1)):
+ super(Conv_bn_dynamic_apool, self).__init__()
+ self.conv = nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size,
+ stride=stride, padding=padding)
+ nn.init.orthogonal_(self.conv.weight)
+ self.bn = nn.BatchNorm2d(out_channels, eps=eps, momentum=momentum, affine=affine,
+ track_running_stats=track_running_states)
+ self.act = nn.ReLU()
+ self.pooling = nn.MaxPool2d((2,2),stride=(2,2),padding=pool_padding)
+ self.gapool = nn.AdaptiveAvgPool2d((1,1))
+
+ def forward(self, inp):
+ output = self.conv(inp)
+ output = self.bn(output)
+ output = self.act(output)
+ # print(output.shape)
+ output = self.pooling(output)
+ output = self.gapool(output)
+ output = output.squeeze()
+ if len(output.shape) < 4:
+ output = output.unsqueeze(0)
+ # print(output.shape)
+ return output
+class Cnn_model(nn.Module):
+
+ def __init__(self,device):
+ super(Cnn_model,self).__init__()
+ inp = torch.zeros([3,100,100])
+ inp=inp.unsqueeze(0)
+ self.psa = PatchSampleAttention.PatchSampleAttention(device).to(device)
+ self.layer1 = Conv_bn_pool(in_channels=inp.size(1), out_channels=6, kernel_size=(3, 3), stride=(1, 1),
+ padding=(1, 0), pool='max', pool_size=(2,2), pool_stride=(2,2),pool_padding=(0,0))
+ self.layer2 = Conv_bn_pool(in_channels=6, out_channels=12, kernel_size=(3, 3), stride=(1, 1),
+ padding=(1, 1), pool='max', pool_size=(2,2), pool_stride=(2,2),pool_padding=(0,0))
+ self.layer3 = Conv_bn_pool(in_channels=12, out_channels=12, kernel_size=(3, 3), stride=(1, 1),
+ padding=(1, 1))
+ # self.layer3_2 = Conv_bn_pool(in_channels=12, out_channels=12, kernel_size=(3, 3), stride=(1, 1),
+ # padding=(1, 0), pool='max', pool_size=(2,2), pool_stride=(2,2),pool_padding=(1,1))
+ # self.layer4 = Conv_bn_dynamic_apool(in_channels=12, out_channels=12, kernel_size=(3, 3), stride=(1, 2),
+ # padding=(1, 1))
+ # self.layer5 = nn.Linear(12,n_classes,bias=True)
+ # nn.init.orthogonal_(self.layer5.weight)
+ # self.layer6 = nn.Linear(128,n_classes,bias=True)
+ # nn.init.orthogonal_(self.layer6.weight)
+ # self.layer7 = Conv_bn_dynamic_apool(in_channels=256, out_channels=256, kernel_size=(1, 1), stride=(1, 1),
+ # padding=(0, 0))
+ # self.layer8 = Conv_bn_pool(in_channels=256, out_channels=512, kernel_size=(1, 1), stride=(1, 1),
+ # padding=(0, 0))
+ # #归一化
+ self.dropout = nn.Dropout(0.1)
+ self.act = nn.ReLU()
+ # # self.layer8= Conv_bn_pool(in_channels=1024, out_channels=1024, kernel_size=(1, 1), stride=(1, 1),
+ # # padding=(0, 0))
+ # self.layer9 = Conv_bn_pool(in_channels=512, out_channels=n_classes, kernel_size=(1, 1), stride=(1, 1),
+ # padding=(0, 0))
+ # self.criterion = nn.CrossEntropyLoss()
+
+ def forward(self, inp, tgt, device,size,is_train):
+ if is_train:
+ self.psa.train()
+ puzzle = torch.zeros([inp.shape[0],size**2,inp.shape[1],inp.shape[2]//size,inp.shape[3]//size])
+ puzzle_side = torch.zeros([inp.shape[0],size**2,4,inp.shape[1],4,inp.shape[3]//size])
+ puzzle_1 = inp.chunk(size,dim=-2)
+ for i in range(size):
+ puzzle_2 = puzzle_1[i].chunk(size,dim=-1)
+ for j in range(size):
+ puzzle[:,i*size+j] = puzzle_2[j].clone()
+ puzzle_side[:,i*size+j,0] = puzzle_2[j][:,:,:4,:].clone()
+ puzzle_side[:,i*size+j,1] = puzzle_2[j][:,:,-4:,:].clone()
+ puzzle_side[:,i*size+j,2] = puzzle_2[j][:,:,:,:4].transpose(2,3).clone()
+ puzzle_side[:,i*size+j,3] = puzzle_2[j][:,:,:,-4:].transpose(2,3).clone()
+
+ s = puzzle_side.shape[:-3]
+ puzzle_side = puzzle_side.view([-1,*puzzle_side.shape[-3:]]).to(device)
+ x = self.layer1(puzzle_side)
+ x = self.layer2(x)
+ x = self.layer3(x)
+ # x = self.layer3_2(x)
+ # 归一化
+ x = F.normalize(x,p=2,dim=0,eps=1e-12)
+ x = x.view([*s,*x.shape[1:]]).squeeze(-2)
+ # x = self.layer4(x)
+ # # print(x.shape)
+ # x = self.layer5(x)
+ # x = self.dropout(x)
+ # x = self.act(x)
+ # x = self.dropout(x)
+
+ # x = self.layer6(x)
+ # print(x.shape)
+
+ batch_range = range(x.shape[0])
+
+ alpha_tb,index_tb = self.psa(x[:,:,0],x[:,:,1])
+ alpha_lr,index_lr = self.psa(x[:,:,2],x[:,:,3])
+ # print(alpha_tb[0])
+ # print(alpha_lr[0])
+ mask = torch.eye(x.size(1)).unsqueeze(0).expand(x.size(0),-1,-1)*0.07
+ tgt_tb = torch.full(alpha_tb.shape,0.03) + mask
+ tgt_lr = torch.full(alpha_lr.shape,0.03) + mask
+ neighbor_t = torch.zeros([x.shape[0],size*(size-1)])
+ neighbor_b = torch.zeros([x.shape[0],size*(size-1)])
+ neighbor_l = torch.zeros([x.shape[0],size*(size-1)])
+ neighbor_r = torch.zeros([x.shape[0],size*(size-1)])
+ for i in range(size):
+ for j in range(size-1):
+ neighbor_t[:,i*(size-1)+j] = tgt[:,i*(size-1)+j]
+ neighbor_b[:,i*(size-1)+j] = tgt[:,i*(size-1)+j+1]
+ for i in range(size):
+ for j in range(size-1):
+ neighbor_l[:,i*(size-1)+j] = tgt[:,j*(size-1)+i]
+ neighbor_r[:,i*(size-1)+j] = tgt[:,j*(size-1)+i+1]
+
+ # neighbor_t = neighbor_t.tolist()
+ # neighbor_b = neighbor_b.tolist()
+ # neighbor_l = neighbor_l.tolist()
+ # neighbor_r = neighbor_r.tolist()
+ # print(neighbor_r[:10][1])
+ # exit()
+ for i in range(size*(size-1)):
+ tgt_tb[batch_range,neighbor_t[:,i].tolist(),neighbor_b[:,i].tolist()] = 0.5
+ tgt_tb[batch_range,neighbor_b[:,i].tolist(),neighbor_t[:,i].tolist()] = 0.5
+ tgt_lr[batch_range,neighbor_l[:,i].tolist(),neighbor_r[:,i].tolist()] = 0.5
+ tgt_lr[batch_range,neighbor_r[:,i].tolist(),neighbor_l[:,i].tolist()] = 0.5
+
+ alpha_tb = torch.flatten(alpha_tb,1,2).to(device)
+ alpha_lr = torch.flatten(alpha_lr,1,2).to(device)
+ tgt_tb = torch.flatten(tgt_tb,1,2).to(device)
+ tgt_lr = torch.flatten(tgt_lr,1,2).to(device)
+ tgt_tb = F.softmax(tgt_tb)
+ tgt_lr = F.softmax(tgt_lr)
+
+ return alpha_tb,alpha_lr,tgt_tb,tgt_lr
+
+
+
+if __name__ == "__main__":
+ model = Cnn_model(torch.device('cuda'))
+ data = torch.rand([8,1,28,28])
+ label = torch.rand([8])
+ logits,acc = model(data,label,8,10)
+
diff --git a/report_05_Jigsaw-Puzzle/PatchSampleAttention.py b/report_05_Jigsaw-Puzzle/PatchSampleAttention.py
new file mode 100644
index 0000000000000000000000000000000000000000..bbf19ee1f76f26341e13f7a192426d03098aef01
--- /dev/null
+++ b/report_05_Jigsaw-Puzzle/PatchSampleAttention.py
@@ -0,0 +1,70 @@
+import torch
+import torch.nn as nn
+import torch.nn.functional as F
+import math
+
+class PatchSampleAttention(nn.Module):
+ def __init__(self,device):
+ super(PatchSampleAttention, self).__init__()
+ self.key_layer = nn.Sequential(nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1, padding=1),
+ # nn.MaxPool2d(kernel_size=2,stride=2,padding=0),
+ # nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1, padding=1),
+ # nn.MaxPool2d(kernel_size=2,stride=2,padding=0)
+ )
+ self.query_layer = nn.Sequential(nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1, padding=1),
+ # nn.MaxPool2d(kernel_size=2,stride=2,padding=0),
+ # nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1, padding=1),
+ # nn.MaxPool2d(kernel_size=2,stride=2,padding=0)
+ )
+ self.value_layer = nn.Sequential(nn.Conv2d(in_channels=1,out_channels=1,kernel_size=3, stride=1, padding=1),
+ # nn.MaxPool2d(kernel_size=2,stride=2,padding=0),
+ # nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1, padding=1),
+ # nn.MaxPool2d(kernel_size=2,stride=2,padding=0)
+ )
+ # self.softmax = nn.Softmax()
+ self.device = device
+
+ def forward(self, feature_in, feature_out):
+ s = feature_in.shape
+ # print(s)
+ feature_in = feature_in.reshape(-1, 1, s[-2], s[-1])
+ feature_out = feature_out.reshape(-1, 1, s[-2], s[-1])
+ key = self.key_layer(feature_in).view(*s[:2],-1)
+ # key = feature_in.clone()
+ query = self.query_layer(feature_out).view(*s[:2],-1)
+ # query = feature_in.clone()
+ value = feature_in.clone()
+ value = self.value_layer(value).view(*s[:2],-1)
+ d = s[1]
+ # query, key, value = query.transpose(0, 1).unsqueeze(1), key.transpose(0, 1).unsqueeze(0), value.transpose(0, 1)
+ alpha = torch.bmm(query.permute(0,1,2),key.permute(0,2,1))
+ s_ = alpha.shape
+ base = torch.full(s_,1e-8).to(self.device)
+ q = torch.mean(query,-1)
+ k = torch.mean(key,-1)
+ for i in range(s[1]):
+ for j in range(s[1]):
+ base[:,i,j] = q[:,i]+k[:,j]
+ # print("base:",base[0])
+ alpha = (alpha/base).flatten(1,2)
+ # print(alpha.shape)
+ alpha = F.softmax(alpha, dim=1)
+ # mask = 1 - torch.eye(alpha.size(-1)).unsqueeze(0).expand(alpha.size(0),-1,-1).to(self.device)
+ # alpha = alpha * mask
+ a = alpha.chunk(s_[2],1)
+ alpha = torch.zeros(s_).to(self.device)
+ for i in range(s_[2]):
+ alpha[:,:,i]=a[i]
+ attention = torch.bmm(alpha, value)
+ # feature_s = torch.sum(feature,dim=0)
+ _,indics = torch.sort(torch.sum(attention, 2), descending=True)
+
+ return alpha,indics
+
+if __name__ == "__main__":
+ feature_in = torch.zeros([32,9,12,24])
+ feature_out = torch.zeros([32,9,12,24])
+ psa = PatchSampleAttention(torch.device('cuda'))
+ alpha, indics = psa(feature_in,feature_out)
+ print(alpha.shape)
+ print(indics.shape)
\ No newline at end of file
diff --git a/report_05_Jigsaw-Puzzle/__pycache__/CNN_model.cpython-38.pyc b/report_05_Jigsaw-Puzzle/__pycache__/CNN_model.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..80d53e17ebbc61402979ac615b933cac59000575
Binary files /dev/null and b/report_05_Jigsaw-Puzzle/__pycache__/CNN_model.cpython-38.pyc differ
diff --git a/report_05_Jigsaw-Puzzle/__pycache__/PatchSampleAttention.cpython-38.pyc b/report_05_Jigsaw-Puzzle/__pycache__/PatchSampleAttention.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..75c404e116681d0a008f0d1cb0d121381b5054f2
Binary files /dev/null and b/report_05_Jigsaw-Puzzle/__pycache__/PatchSampleAttention.cpython-38.pyc differ
diff --git a/report_05_Jigsaw-Puzzle/__pycache__/load_data.cpython-38.pyc b/report_05_Jigsaw-Puzzle/__pycache__/load_data.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..bceaeb1bda15147a9e35c6d940decb9b43f8fc83
Binary files /dev/null and b/report_05_Jigsaw-Puzzle/__pycache__/load_data.cpython-38.pyc differ
diff --git a/report_05_Jigsaw-Puzzle/__pycache__/sort.cpython-38.pyc b/report_05_Jigsaw-Puzzle/__pycache__/sort.cpython-38.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..1c704f3621d3d64d8e1588a82f95708bf2c940ef
Binary files /dev/null and b/report_05_Jigsaw-Puzzle/__pycache__/sort.cpython-38.pyc differ
diff --git a/report_05_Jigsaw-Puzzle/data/puzzle_3x3/.floyddata b/report_05_Jigsaw-Puzzle/data/puzzle_3x3/.floyddata
new file mode 100644
index 0000000000000000000000000000000000000000..54cdd56402183c447095830defbd288f3656cb54
--- /dev/null
+++ b/report_05_Jigsaw-Puzzle/data/puzzle_3x3/.floyddata
@@ -0,0 +1 @@
+{"name": "jigsaw", "data_endpoint": null, "family_id": "pAJncn3SRbabAW4m757Lin", "namespace": "shivaverma", "data_id": null, "resource_id": null, "data_name": "shivaverma/datasets/jigsaw/1", "tarball_path": null}
\ No newline at end of file
diff --git a/report_05_Jigsaw-Puzzle/load_data.py b/report_05_Jigsaw-Puzzle/load_data.py
new file mode 100644
index 0000000000000000000000000000000000000000..f8459e5ecd53c22712e5cfda79e337c388f26a4b
--- /dev/null
+++ b/report_05_Jigsaw-Puzzle/load_data.py
@@ -0,0 +1,33 @@
+import os
+import numpy as np
+from PIL import Image
+
+
+def load_puzzle(images_path = './data/puzzle_2x2/train',labels_path = './data/puzzle_2x2/train.csv',index = [0,8]):
+ """Load MNIST data from `path`"""
+ pathlist = os.listdir(images_path)
+ images = []
+ with open(labels_path,mode='r') as f:
+ labels = f.readlines()[index[0]+1:index[1]+1]
+ # print(labels)
+ for i,l in enumerate(labels):
+ # print(i)
+ labels[i] = l.split(',')[-1].split('\n')[0].split()
+ for j,n in enumerate(labels[i]):
+ labels[i][j] = int(n)
+ labels = np.array(labels)
+ for i,pi in enumerate(pathlist[index[0]:index[1]]):
+ # print(i)
+ ab_path = os.path.join(images_path,pi)
+ I = Image.open(ab_path)
+ images += [np.array(I)]
+ images = np.array(images)
+
+ return images, labels, len(pathlist)
+
+if __name__ == "__main__":
+ data = load_puzzle('./data/puzzle_2x2/train','./data/puzzle_2x2/train.csv',[0,8])
+ images = data[0]
+ labels = data[1]
+ print(images.shape)
+ print(labels.shape)
diff --git a/report_05_Jigsaw-Puzzle/main.py b/report_05_Jigsaw-Puzzle/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..773cd66d351bb507ffb139d8797c0c2409d4990f
--- /dev/null
+++ b/report_05_Jigsaw-Puzzle/main.py
@@ -0,0 +1,144 @@
+import numpy as np
+import torch
+import torch.nn as nn
+from torch import optim
+import load_data
+import CNN_model
+import sort
+
+def train(iden_class,epochs,bsz,lr,weight_decay,images_path,labels_path,save_model):
+
+ device = torch.device('cuda')
+ _,_,length = load_data.load_puzzle(images_path, labels_path)
+
+ # rand_num = torch.randperm(length//bsz*bsz)
+ #
+ # rand_num = rand_num.reshape(-1,bsz)
+
+ model = CNN_model.Cnn_model(device).to(device)
+ # torch.save(model, "model_2.bin")
+
+ optimizer = optim.Adam(model.parameters(),lr=lr,betas=(0.9,0.999),eps=1e-8,weight_decay=weight_decay)
+ # scheduler = lr_scheduler.ExponentialLR(optimizer, gamma=0.9)
+ criterion = nn.MSELoss()
+
+ train_loss_p = []
+ # train_acc_p = []
+ # acc_highest = 0
+
+ for epoch in range(epochs):
+ loss = 0
+ # acc=0
+ # acc_high=0
+ print("epoch:",epoch + 1,"/",epochs)
+ model.train()
+ for batch_id in range(length//bsz):
+ data = load_data.load_puzzle(images_path, labels_path, [batch_id*bsz,(batch_id+1)*bsz])
+
+ batch_image = torch.tensor(data[0]).permute(0,3,1,2).float().to(device)
+ batch_label = torch.tensor(data[1]).float().to(device)
+
+ print("epoch",epoch + 1," batch:",batch_id + 1,"/",length//bsz,end=" ")
+ alpha_tb,alpha_lr,tgt_tb,tgt_lr = model(batch_image, batch_label, device, iden_class, True)
+
+ loss_batch = criterion(alpha_tb, tgt_tb).to(device) + criterion(alpha_lr, tgt_lr).to(device)
+ print("loss: ", loss_batch.item(),end="\n")
+ # print("acc: {:.0%}".format(acc_batch))
+ train_loss_p.append(loss_batch.item())
+ # train_acc_p.append(acc_batch)
+
+ # acc_high = acc_batch if acc_batch > acc_high else acc_high
+
+ loss += loss_batch.item()
+ # acc += acc_batch
+
+ optimizer.zero_grad()
+ loss_batch.backward()
+ optimizer.step()
+
+ # scheduler.step(epoch)
+
+ loss /= length//bsz
+ # acc /= length//bsz
+ # if acc >= acc_highest:
+ # acc_highest = acc
+ if save_model:
+ torch.save(model, save_model + "_" + str(iden_class) + ".bin")
+ state_dic = {
+ "state": model.state_dict(),
+ "epoch": epoch
+ }
+
+ print("epoch",epoch + 1,": train_loss:",loss)
+ # print("epoch",epoch + 1,": train_loss:",loss," train_acc:{:.0%}".format(acc)," train_acc_high:",acc_high)
+
+ # return acc_highest
+
+def test(epoch,iden_class,model_path,images_path,labels_path,bsz=1):
+
+ device = torch.device('cuda')
+
+ _,_,length = load_data.load_puzzle(images_path,labels_path)
+
+ model = torch.load(model_path + "_" + str(iden_class) + ".bin")
+
+ # scheduler = lr_scheduler.ExponentialLR(optimizer, gamma=0.9)
+ criterion = nn.MSELoss()
+
+ test_loss_p = []
+ # test_acc_p = []
+
+ acc = 0
+ loss = 0
+ model.eval()
+ for batch_id in range(length//bsz):
+ data = load_data.load_puzzle(images_path,labels_path,[batch_id*bsz,(batch_id+1)*bsz])
+
+ batch_image = torch.tensor(data[0]).permute(0,3,1,2).to(device).float()
+ batch_label = torch.tensor(data[1]).to(device).float()
+ with torch.no_grad():
+ alpha_tb,alpha_lr,tgt_tb,tgt_lr = model(batch_image, batch_label, device, iden_class,False)
+
+ sort_tb,index_tb = torch.sort(alpha_tb,dim=-1, descending=True)
+ sort_lr,index_lr = torch.sort(alpha_lr,dim=-1, descending=True)
+
+ sort_tb,index_tb = sort_tb.cpu().detach().numpy().tolist(),index_tb.cpu().detach().numpy().tolist()
+ sort_lr,index_lr = sort_lr.cpu().detach().numpy().tolist(),index_lr.cpu().detach().numpy().tolist()
+ batch_label = batch_label.int().cpu().detach().numpy().tolist()
+
+ for i in range(bsz):
+ beta_tb = []
+ beta_lr = []
+ for j in range(iden_class**4):
+ beta_tb.append([sort_tb[i][j],index_tb[i][j]//iden_class**2,index_tb[i][j]%iden_class**2])
+ beta_lr.append([sort_lr[i][j],index_lr[i][j]//iden_class**2,index_lr[i][j]%iden_class**2])
+ order,sign = sort.stack(iden_class,beta_tb,beta_lr)
+ # print(order)
+ # print(batch_label[i])
+ if order == batch_label[i]:
+ acc += 1
+ acc /= bsz
+
+ loss_batch = criterion(alpha_tb, tgt_tb).to(device) + criterion(alpha_lr, tgt_lr).to(device)
+ test_loss_p.append(loss_batch.item())
+ # test_acc_p.append(acc_batch)
+
+
+ loss += loss_batch.item()
+ # acc += acc_batch
+
+
+ # scheduler.step(epoch)
+
+ loss /= length//bsz
+ acc /= length//bsz
+
+ print("epoch",epoch,": test_loss:",loss," test_acc:{:.0%}".format(acc))
+
+
+def main(iden_class,epochs,bsz,lr,weight_decay,train_images_path,train_labels_path,test_images_path,test_labels_path,save_model):
+ train(iden_class,epochs,bsz,lr,weight_decay,train_images_path,train_labels_path,save_model)
+ test(epochs,iden_class,save_model,test_images_path,test_labels_path)
+
+if __name__ == "__main__":
+ main(2,0,64,1e-5,5e-5,'data/puzzle_2x2/train','data/puzzle_2x2/train.csv','data/puzzle_2x2/test','data/puzzle_2x2/test.csv','model')
\ No newline at end of file
diff --git a/report_05_Jigsaw-Puzzle/model_2.bin b/report_05_Jigsaw-Puzzle/model_2.bin
new file mode 100644
index 0000000000000000000000000000000000000000..84ca4e5cfe88aff2652e09b084b2c3442ed3379a
Binary files /dev/null and b/report_05_Jigsaw-Puzzle/model_2.bin differ
diff --git a/report_05_Jigsaw-Puzzle/model_3.bin b/report_05_Jigsaw-Puzzle/model_3.bin
new file mode 100644
index 0000000000000000000000000000000000000000..6cbafc238a7f6201ef39e6108b95c7ed1899533e
Binary files /dev/null and b/report_05_Jigsaw-Puzzle/model_3.bin differ
diff --git a/report_05_Jigsaw-Puzzle/report_template.ipynb b/report_05_Jigsaw-Puzzle/report_template.ipynb
index 1db93a237a306712d7e535c5211c86db247a264e..bd566ebdf214bf277b19f10659bbac32a7784113 100644
--- a/report_05_Jigsaw-Puzzle/report_template.ipynb
+++ b/report_05_Jigsaw-Puzzle/report_template.ipynb
@@ -4,33 +4,79 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "# Report - 报告题目\n",
+ "# Report - Puzzle\n",
"\n",
- "* 姓名\n",
- "* 学号\n",
+ "* 法品哲\n",
+ "* 2022265233\n",
"\n",
"\n",
"## 任务简介\n",
"\n",
- "这里简述一下任务是什么;最终的目标是什么\n",
+ "将一组打乱了的拼图块恢复到原本的位置,拼图分为2*2和3*3两组数据,组成的整个图片尺寸基本一致\n",
+ "这是我认为本课程所有报告中最难也是最有趣的一个任务。\n",
"\n",
"## 解决途径\n",
"\n",
"主要包括:\n",
- "1. 问题的思考,整体的思路\n",
- "2. 选用的方法,以及为何选用这些方法\n",
- "3. 所用方法的详细解释,包括理论上的背景、模型、实现的细节\n",
- "3. 实现过程遇到的问题,以及如何解决的\n",
- "4. 最终的结果,实验分析\n",
+ "1. 首先带入人的思路去考虑这个任务,在拼拼图时,我们一般首先判断图片的整体风格,从散落的拼图中大致地恢复原图的信息,随后浏览拼图的每一个图块,估计它的大致位置;进一步将位置相近,特征相似的图块做对比,不断两两确认拼接;随着拼接数目的增大逐渐覆盖拼图区域,最终将剩余的图块补充完整。\n",
+ "2. 考虑到需要对图像进行特征处理,必然需要用到卷积神经网络,其次,拼图的关键在于视觉信息的相似性,这里使用attention策略来完成,最终会得到一组以相似度排序的对照关系,这时候就需要一个确切的排序算法来给出最终的拼图恢复位置。\n",
+ "3. 具体来说,遍历人类拼图整个过程,首先用一个CNN网络表征最开始对于拼图整体的风格鉴定和数据降维,否则第一无法获得全局信息,第二夸张的数据量会严重拖慢训练进展;随后处理关键点在于相邻图块的两两拼接,以机器视角考虑,就是计算两个图块对应边缘的连续性,为了方便处理,模型直接计算其相似性代替,具体地,将经第一步降维后的图块进一步降维,只保留上、下、左、右四个边缘块,随后喂给一个attention模型,,区分上下和左右计算每个图块对应边缘的相似性;最终将获得的边缘相似度进行排序,设计算法根据边缘相似度以贪心策略进行拼图的最优排列,对照上文中依靠两两拼接逐渐覆盖整个拼图的步骤\n",
+ "4. 不得不说,实现过程基本全是问题,比如按照人解决问题的思路,我最开始的策略是将所有图块区分为中心块、边块和角块,降低可能的组合数量,但在实操过程中发现,CNN对于图片的旋转和对称现象不敏感,这就导致了他无法将位置关系接近的同类块正确排列。(比如对于CNN来说,如下两种排列是等价的,因为每一个块都具有相同的位置关系)所以必须加入上下左右边缘的区分。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "outputs": [],
+ "source": [
+ "a1=[[5,1,6],\n",
+ " [2,0,3],\n",
+ " [7,4,8]]\n",
"\n",
- "要求:\n",
- "1. 数据的基本情况介绍\n",
- "2. 程序,以及各个部分的解释、说明\n",
- "3. 结果的可视化,精度等的分析\n",
+ "a2=[[6,3,8],\n",
+ " [1,0,4],\n",
+ " [5,2,7]]"
+ ],
+ "metadata": {
+ "collapsed": false
+ }
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ " 当然了我也考虑过直接将图片囫囵丢进模型,做九个九分类的任务(对于3*3的拼图任务),但这未免太缺乏先验知识基础,听起来不具有实操意义,故pass掉了。\n",
+ " 另外在实现最终的排序算法时,我着实栽了个大跟头,本来以为就是一个简单的贪心策略算法,但实操起来还是很麻烦的。(感觉可以投稿给下一届CSP当赛题用)\n",
"\n",
+ "5. 最终的结果,实验分析。如下:"
+ ],
+ "metadata": {
+ "collapsed": false
+ }
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "outputs": [],
+ "source": [
+ "import main\n",
+ "main.test(10,2,'model','data/puzzle_2x2/valid','data/puzzle_2x2/valid.csv')"
+ ],
+ "metadata": {
+ "collapsed": false,
+ "pycharm": {
+ "is_executing": true
+ }
+ }
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
"## 总结\n",
- "总结任务实现过程所取得的心得等。"
- ]
+ "很难,但也挺有意义。"
+ ],
+ "metadata": {
+ "collapsed": false
+ }
}
],
"metadata": {
diff --git a/report_05_Jigsaw-Puzzle/sort.py b/report_05_Jigsaw-Puzzle/sort.py
new file mode 100644
index 0000000000000000000000000000000000000000..ee5fe5c204d291284df51a637cbed7756d407eab
--- /dev/null
+++ b/report_05_Jigsaw-Puzzle/sort.py
@@ -0,0 +1,386 @@
+import copy
+from itertools import chain
+import numpy as np
+
+def combin(size,posi_puzs1,posi_puzs2):
+ # print("posi_puzs1:",posi_puzs1)
+ # print("posi_puzs2:",posi_puzs2)
+
+ combin_puzs = []
+ paradox = True
+
+ for i,a in enumerate(posi_puzs1):
+ for j,b in enumerate(posi_puzs2):
+ paradox_ = False
+ combin_puz = []
+ for l in range(size):
+ combin_puz.append([])
+ for r in range(size):
+ # print(a[l][r])
+ # print(b[l][r])
+ if a[l][r] == b[l][r]:
+ combin_puz[l].append(a[l][r])
+ elif b[l][r] == -1:
+ combin_puz[l].append(a[l][r])
+ elif a[l][r] == -1:
+ combin_puz[l].append(b[l][r])
+ else:
+ paradox_ = True
+ break
+ if paradox_:
+ break
+
+ if not paradox_:
+ paradox = False
+ combin_puzs.append(combin_puz)
+
+ # print("combin_puzs:",combin_puzs)
+
+ return copy.deepcopy(combin_puzs),paradox
+
+def arrange(size,sub_puz):
+
+ ln_m = 0
+ for i in range(size):
+ for j in range(size):
+ if sub_puz[size-i-1][j] >= 0:
+ ln_m = size - i
+ break
+ if ln_m > 0:
+ break
+ ro_m = 0
+ for i in range(size):
+ for j in range(size):
+ if sub_puz[j][size-i-1] >= 0:
+ ro_m = size - i
+ break
+ if ro_m > 0:
+ break
+ if ln_m == 1 and ro_m == 1:
+ print("only 1 part of puzzle should not be fed to function \"arrange\".")
+ exit(0)
+ posi_map = []
+ for i in range(size):
+ for j in range(size):
+ if size-ln_m >= i and size-ro_m >= j:
+ posi_map.append([i,j])
+ posi_puzs = []
+ for p in posi_map:
+ posi_puz = []
+ for i in range(size):
+ posi_puz.append([])
+ for j in range(size):
+ if i-p[0] >= 0 and j-p[1] >= 0:
+ posi_puz[i].append(sub_puz[i-p[0]][j-p[1]])
+ else:
+ posi_puz[i].append(-1)
+ posi_puzs.append(posi_puz)
+
+ return copy.deepcopy(posi_puzs)
+
+def add(size,sub_puz,cell_copy,puz_copy,x,j,k_,oversize,mode):
+
+ paradox = False
+ if 0 <= k_[0] < size and 0 <= k_[1] < size:
+ if x[1-j] in cell_copy:
+ if sub_puz[k_[0]][k_[1]] == x[1-j]:
+ pass
+ else:
+ paradox = True
+ elif sub_puz[k_[0]][k_[1]] < 0:
+ cell_copy.append(x[1 - j])
+ puz_copy[k_[0]][k_[1]] = x[1-j]
+ if len(cell_copy) > size-1:
+ oversize = True
+ else:
+ paradox = True
+ elif mode == 1:
+ movable = True
+ if k_[0] < 0:
+ for n in range(size):
+ if sub_puz[size - 1][n] >= 0:
+ movable = False
+ break
+ if movable:
+ line = puz_copy.pop()
+ puz_copy.insert(0, line)
+ k_[0] += 1
+ elif k_[0] >= size:
+ for n in range(size):
+ if sub_puz[0][n] >= 0:
+ movable = False
+ break
+ if movable:
+ line = puz_copy.pop(0)
+ puz_copy.insert(-1, line)
+ k_[0] -= 1
+ elif k_[1] < 0:
+ for n in range(size):
+ if sub_puz[n][size - 1] >= 0:
+ movable = False
+ break
+ if movable:
+ for n in range(len(puz_copy)):
+ row = puz_copy[n].pop()
+ puz_copy[n].insert(0, row)
+ k_[1] += 1
+ elif k_[1] >= size:
+ for n in range(size):
+ if sub_puz[n][0] >= 0:
+ movable = False
+ break
+ if movable:
+ for n in range(len(puz_copy)):
+ row = puz_copy[n].pop(0)
+ puz_copy[n].insert(-1, row)
+ k_[1] -= 1
+ if movable:
+ cell_copy.append(x[1 - j])
+ puz_copy[k_[0]][k_[1]] = x[1 - j]
+ if len(cell_copy) > size-1:
+ oversize = True
+ pass
+ else:
+ paradox = True
+ else:
+ paradox = True
+ if paradox:
+ puz_copy.clear()
+ return paradox,oversize
+
+def stack(size,alpha_tb,alpha_lr):
+ stk = [[],[],[]]
+ oversize = False
+ sign = False
+ while len(alpha_tb) > 0 or len(alpha_lr) > 0:
+ paradox = False
+ m = []
+ for i in range(size):
+ m.append([])
+ for j in range(size):
+ m[i].append(-1)
+
+ if len(alpha_tb) > 0 and len(alpha_lr) > 0:
+ if alpha_tb[0][0] > alpha_lr[0][0]:
+ x = alpha_tb.pop(0)[1:]
+ m[0][0] = x[0]
+ m[1][0] = x[1]
+ else:
+ x = alpha_lr.pop(0)[1:]
+ m[0][0] = x[0]
+ m[0][1] = x[1]
+ elif len(alpha_tb) > 0:
+ x = alpha_tb.pop(0)[1:]
+ m[0][0] = x[0]
+ m[1][0] = x[1]
+ else:
+ x = alpha_lr.pop(0)[1:]
+ m[0][0] = x[0]
+ m[0][1] = x[1]
+
+ if x[0] == x[1]:
+ continue
+
+ is_ext = False
+ stk_copy = copy.deepcopy(stk)
+ # print("0:",stk_copy[0])
+ # print("1:",stk_copy[1])
+ # print("2:",stk_copy[2])
+ # print("m:",m)
+ # print("end")
+ for i,a in enumerate(stk[0]):
+ for j,b in enumerate(x):
+ if b in a:
+ # print(stk[0])
+ # if x[1-j] in a:
+ # paradox = True
+ # break
+ is_ext = True
+ if len(stk_copy[2]) > 0:
+ paradox = True
+ for p,posi_puz in enumerate(stk_copy[2]):
+ # print(stk[0])
+ # print(stk[2])
+ # print(b)
+ # print("end")
+ k = [(s, t.index(b)) for s, t in enumerate(posi_puz) if b in t][0]
+ if m[0][1] < 0:
+ if j == 0:
+ k_ = [k[0]+1,k[1]]
+ else:
+ k_ = [k[0]-1,k[1]]
+ else:
+ if j == 0:
+ k_ = [k[0],k[1]+1]
+ else:
+ k_ = [k[0],k[1]-1]
+ # print(k_)
+ # print(posi_puz)
+ paradox_,oversize = add(size,posi_puz,stk_copy[0][i],stk_copy[2][p],x,j,k_,oversize,2)
+ if not paradox_:
+ paradox = False
+
+ # for i, p in enumerate(stk_copy[2]):
+ # if len(p) == 0:
+ # stk_copy[2].pop(i)
+ s = 0
+ while s < len(stk_copy[2]):
+ if len(stk_copy[2][s]) == 0:
+ stk_copy[2].pop(s)
+ else:
+ s+=1
+
+ else:
+ k = [(s, t.index(b)) for s, t in enumerate(stk[1][i]) if b in t][0]
+ if m[0][1] < 0:
+ if j == 0:
+ k_ = [k[0]+1,k[1]]
+ else:
+ k_ = [k[0]-1,k[1]]
+ else:
+ if j == 0:
+ k_ = [k[0],k[1]+1]
+ else:
+ k_ = [k[0],k[1]-1]
+ paradox,oversize = add(size,stk[1][i],stk_copy[0][i],stk_copy[1][i],x,j,k_,oversize,1)
+
+ break
+
+ if is_ext or paradox:
+ break
+
+ if is_ext or paradox:
+ break
+
+ if paradox:
+ continue
+
+
+ if not is_ext:
+ stk_copy[0].append(x)
+ stk_copy[1].append(m)
+ if len(stk_copy[0]) < size+1 and not oversize:
+ pass
+ else:
+ sub_puzs = []
+ posi_puzs = []
+ if len(stk_copy[2]) > 0:
+ posi_puzs.append(stk_copy[2])
+ else:
+ posi_puzs.append(arrange(size,stk_copy[1][0]))
+ for i,a in enumerate(stk_copy[1][1:]):
+ # sub_puzs.append(a)
+ sub_puzs.append(arrange(size,a))
+
+ # stk_copy[2] = []
+ for i,a in enumerate(sub_puzs):
+ stk_copy[2],paradox = combin(size,posi_puzs[0],sub_puzs[i])
+ posi_puzs[0] = stk_copy[2]
+ if len(stk_copy[2]) == 0:
+ continue
+ else:
+ stk_copy[0] = [copy.deepcopy(list(chain(*stk_copy[0])))]
+ if len(stk_copy[0][0]) > size-1:
+ oversize = True
+ else:
+ exit_same = False
+ i = 0
+ j = 0
+ while i < len(stk_copy[0]):
+ while j < len(stk_copy[0][i+1:]):
+ b = stk_copy[0][j+i+1]
+ for k,c in enumerate(b):
+ if c in stk_copy[0][i] or oversize:
+ exit_same = True
+ if exit_same:
+ sub_puzs = arrange(size,stk_copy[1][j+i+1])
+ if len(stk_copy[2]) > 0:
+ posi_puzs = stk_copy[2]
+ else:
+ posi_puzs = arrange(size,stk_copy[1][i])
+
+ # print(stk_copy[0])
+ # print(stk_copy[1])
+ # print(stk_copy[2])
+ # print(posi_puzs)
+ stk_copy[2],paradox = combin(size,posi_puzs,sub_puzs)
+ # print(stk_copy[2])
+ # print(paradox)
+ # print("eend")
+ # print(a)
+ # print(stk_copy[0][i])
+ # print(b)
+ # print(stk_copy[0][i]+[t for t in b if t not in a])
+ stk_copy[0][i]+=[t for t in b if t not in stk_copy[0][i]]
+ # print(stk_copy[0][i])
+ stk_copy[0].pop(j+i+1)
+ stk_copy[1].pop(j+i+1)
+
+ if not oversize and len(stk_copy[0][i]) > size-1:
+ oversize = True
+ i = 0
+ j = 1
+ continue
+ else:
+ j += 1
+ i += 1
+ # if exit_same:
+ # break
+ if paradox:
+ continue
+
+ # for i,p in enumerate(stk_copy[2]):
+ # if len(p) == 0:
+ # stk_copy[2].pop(i)
+ s = 0
+ while s < len(stk_copy[2]):
+ if len(stk_copy[2][s]) == 0:
+ stk_copy[2].pop(s)
+ else:
+ s+=1
+
+ stk = copy.deepcopy(stk_copy)
+ if len(stk[0][0]) > size-1:
+ oversize = True
+ # print(stk[0])
+ if len(stk[0][0]) > size**2-2:
+ if len(stk[2]) == 1:
+ sign = True
+ break
+ elif len(stk[2]) == 0 and len(stk[1]) == 1:
+ sign = True
+ break
+ # print(paradox)
+ # print(is_ext)
+ # print(oversize)
+ # print(stk)
+ # exit()
+
+
+ if len(stk[2]) > 0:
+ order = list(chain(*stk[2][0]))
+ else:
+ order = list(chain(*stk[1][0]))
+ if -1 in order:
+ order[order.index(-1)] = int(size**2*(size**2-1)/2)-1-sum(order)
+
+ return order,sign
+
+if __name__ == "__main__":
+ size = 3
+
+ tb = np.random.rand(size**4)
+ sort_tb = np.argsort(tb)[::-1]
+ alpha_tb = []
+ for i,a in enumerate(sort_tb):
+ alpha_tb .append([tb[a],a//size**2,a%size**2])
+
+ lr = np.random.rand(size**4)
+ sort_lr = np.argsort(lr)[::-1]
+ alpha_lr = []
+ for i,a in enumerate(sort_lr):
+ alpha_lr.append([lr[a],a//size**2,a%size**2])
+
+ order,sign = stack(size,alpha_tb,alpha_lr)
+ order.sort()
+ print(order)
diff --git a/report_05_Jigsaw-Puzzle/test.py b/report_05_Jigsaw-Puzzle/test.py
new file mode 100644
index 0000000000000000000000000000000000000000..f83c2c44e50d11067e6bc472eba24ddc45184531
--- /dev/null
+++ b/report_05_Jigsaw-Puzzle/test.py
@@ -0,0 +1,8 @@
+import torch
+
+def dele(s):
+ s.clear()
+
+array1 = [["banana", "yam"],["mango", "apple"]]
+array2 = [["banana", "yam"],["mango"]]
+print(array1 == array2)
\ No newline at end of file