├── Keras └── multi-lable classification │ ├── classify.py │ ├── models │ ├── __init__.py │ └── smallervggnet.py │ ├── search_bing_api.py │ └── train.py ├── Notes ├── Deep Rectangling for Image Stitching.docx ├── Depth_Aware_Multi-Grid.docx ├── LeveragingLine-pointConsistenceNotes.md ├── LeveragingLine-pointConsistenceNotes.pdf ├── NeuralRenderingnNotes.md ├── Qualcomm_RealTime_Accurate_ConsistentVideoSemanticSegmentationNotes.docx ├── Quantization_Notes.pdf ├── 人脸相关学习笔记.md ├── 常用功能函数代码.md ├── 数学基础.md ├── 机器学习基础.md ├── 目标检测学习笔记.md └── 经典的目标检测网络学习笔记.md ├── Practise ├── Apriori.py ├── Loss_functions_practise.py └── Perfermance_measures.py ├── Pytorch ├── PyTorch 常用代码合集.md ├── PyTorch 资源&教程&项目.md ├── PyTorch学习笔记.md ├── create_landmark_dataset.py ├── neural_networks_pytorch.ipynb ├── practise │ ├── autograd.ipynb │ ├── basic_practise.ipynb │ ├── basic_practise.py │ ├── neural_network.ipynb │ └── train_classifier_example.ipynb ├── pytorch_dataloader_tutorial.ipynb └── pytorch_transfer_learning_example.ipynb ├── README.md ├── Tutorials └── machineLearning_Python │ ├── README.md │ ├── basic_cnn.py │ ├── classify_images.py │ ├── classify_irs.py │ └── nn_iris.py ├── images ├── SSD_8.png └── SSD_9.png ├── 编程基础 └── 编程相关教程&资源&工具&博客网站.md └── 资源&教程&项目.md /Keras/multi-lable classification/classify.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*-coding:utf-8 -*- 3 | """ 4 | @Author: Luocai 5 | @file: classify.py 6 | @time: 2019/07/13 16:09 7 | @desc: 8 | 9 | 分类预测 10 | """ 11 | # import the necessary packages 12 | from keras.preprocessing.image import img_to_array 13 | from keras.models import load_model 14 | import numpy as np 15 | import argparse 16 | import imutils 17 | import pickle 18 | import cv2 19 | import os 20 | 21 | # construct the argument parse and parse the arguments 22 | ap = argparse.ArgumentParser() 23 | ap.add_argument("-m", "--model", required=True, 24 | help="path to trained model model") 25 | ap.add_argument("-l", "--labelbin", required=True, 26 | help="path to label binarizer") 27 | ap.add_argument("-i", "--image", required=True, 28 | help="path to input image") 29 | args = vars(ap.parse_args()) 30 | 31 | # load the image 32 | image = cv2.imread(args["image"]) 33 | output = imutils.resize(image, width=400) 34 | 35 | # pre-process the image for classification 36 | image = cv2.resize(image, (96, 96)) 37 | image = image.astype("float") / 255.0 38 | image = img_to_array(image) 39 | image = np.expand_dims(image, axis=0) 40 | 41 | # load the trained convolutional neural network and the multi-label 42 | # binarizer 43 | print("[INFO] loading network...") 44 | model = load_model(args["model"]) 45 | mlb = pickle.loads(open(args["labelbin"], "rb").read()) 46 | 47 | # classify the input image then find the indexes of the two class 48 | # labels with the *largest* probability 49 | print("[INFO] classifying image...") 50 | proba = model.predict(image)[0] 51 | idxs = np.argsort(proba)[::-1][:2] 52 | 53 | # loop over the indexes of the high confidence class labels 54 | for (i, j) in enumerate(idxs): 55 | # build the label and draw the label on the image 56 | label = "{}: {:.2f}%".format(mlb.classes_[j], proba[j] * 100) 57 | cv2.putText(output, label, (10, (i * 30) + 25), 58 | cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) 59 | 60 | # show the probabilities for each of the individual labels 61 | for (label, p) in zip(mlb.classes_, proba): 62 | print("{}: {:.2f}%".format(label, p * 100)) 63 | 64 | # show the output image 65 | cv2.imshow("Output", output) 66 | cv2.waitKey(0) 67 | 68 | -------------------------------------------------------------------------------- /Keras/multi-lable classification/models/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*-coding:utf-8 -*- 3 | """ 4 | @Author: Luocai 5 | @file: train.py 6 | @time: 2019/07/13 15:51 7 | @desc: 8 | 9 | 10 | """ -------------------------------------------------------------------------------- /Keras/multi-lable classification/models/smallervggnet.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*-coding:utf-8 -*- 3 | """ 4 | @Author: Luocai 5 | @file: smallervggnet.py 6 | @time: 2019/07/13 15:44 7 | @desc: 8 | 9 | 对 VGGNET 的修改,网络层数量更少 10 | """ 11 | from keras.models import Sequential 12 | from keras.layers.normalization import BatchNormalization 13 | from keras.layers.convolutional import Conv2D 14 | from keras.layers.convolutional import MaxPooling2D 15 | from keras.layers.core import Activation 16 | from keras.layers.core import Flatten 17 | from keras.layers.core import Dropout 18 | from keras.layers.core import Dense 19 | from keras import backend as K 20 | 21 | 22 | class SmallerVGGNet: 23 | @staticmethod 24 | def build(width, height, depth, classes, finalAct="softmax"): 25 | # initialize the model along with the input shape to be 26 | # "channels last" and the channels dimension itself 27 | model = Sequential() 28 | inputShape = (height, width, depth) 29 | chanDim = -1 30 | 31 | # if we are using "channels first", update the input shape 32 | # and channels dimension 33 | if K.image_data_format() == "channels_first": 34 | inputShape = (depth, height, width) 35 | chanDim = 1 36 | # CONV => RELU => POOL 37 | model.add(Conv2D(32, (3, 3), padding="same", 38 | input_shape=inputShape)) 39 | model.add(Activation("relu")) 40 | model.add(BatchNormalization(axis=chanDim)) 41 | model.add(MaxPooling2D(pool_size=(3, 3))) 42 | model.add(Dropout(0.25)) 43 | 44 | # (CONV => RELU) * 2 => POOL 45 | model.add(Conv2D(64, (3, 3), padding="same")) 46 | model.add(Activation("relu")) 47 | model.add(BatchNormalization(axis=chanDim)) 48 | model.add(Conv2D(64, (3, 3), padding="same")) 49 | model.add(Activation("relu")) 50 | model.add(BatchNormalization(axis=chanDim)) 51 | model.add(MaxPooling2D(pool_size=(2, 2))) 52 | model.add(Dropout(0.25)) 53 | 54 | # (CONV => RELU) * 2 => POOL 55 | model.add(Conv2D(128, (3, 3), padding="same")) 56 | model.add(Activation("relu")) 57 | model.add(BatchNormalization(axis=chanDim)) 58 | model.add(Conv2D(128, (3, 3), padding="same")) 59 | model.add(Activation("relu")) 60 | model.add(BatchNormalization(axis=chanDim)) 61 | model.add(MaxPooling2D(pool_size=(2, 2))) 62 | model.add(Dropout(0.25)) 63 | 64 | # first (and only) set of FC => RELU layers 65 | model.add(Flatten()) 66 | model.add(Dense(1024)) 67 | model.add(Activation("relu")) 68 | model.add(BatchNormalization()) 69 | model.add(Dropout(0.5)) 70 | 71 | # use a *softmax* activation for single-label classification 72 | # and *sigmoid* activation for multi-label classification 73 | model.add(Dense(classes)) 74 | model.add(Activation(finalAct)) 75 | 76 | # return the constructed network architecture 77 | return model -------------------------------------------------------------------------------- /Keras/multi-lable classification/search_bing_api.py: -------------------------------------------------------------------------------- 1 | # USAGE 2 | # python search_bing_api.py --query "blue jeans" --output dataset/blue_jeans 3 | # python search_bing_api.py --query "blue dress" --output dataset/blue_dress 4 | # python search_bing_api.py --query "red dress" --output dataset/red_dress 5 | # python search_bing_api.py --query "red shirt" --output dataset/red_shirt 6 | # python search_bing_api.py --query "blue shirt" --output dataset/blue_shirt 7 | # python search_bing_api.py --query "black jeans" --output dataset/black_jeans 8 | 9 | # import the necessary packages 10 | from requests import exceptions 11 | import argparse 12 | import requests 13 | import cv2 14 | import os 15 | 16 | # construct the argument parser and parse the arguments 17 | ap = argparse.ArgumentParser() 18 | ap.add_argument("-q", "--query", required=True, 19 | help="search query to search Bing Image API for") 20 | ap.add_argument("-o", "--output", required=True, 21 | help="path to output directory of images") 22 | args = vars(ap.parse_args()) 23 | 24 | # set your Microsoft Cognitive Services API key along with (1) the 25 | # maximum number of results for a given search and (2) the group size 26 | # for results (maximum of 50 per request) 27 | API_KEY = "INSERT_YOUR_API_KEY_HERE" 28 | MAX_RESULTS = 400 29 | GROUP_SIZE = 50 30 | 31 | # set the endpoint API URL 32 | URL = "https://api.cognitive.microsoft.com/bing/v7.0/images/search" 33 | 34 | # when attemping to download images from the web both the Python 35 | # programming language and the requests library have a number of 36 | # exceptions that can be thrown so let's build a list of them now 37 | # so we can filter on them 38 | EXCEPTIONS = set([IOError, FileNotFoundError, 39 | exceptions.RequestException, exceptions.HTTPError, 40 | exceptions.ConnectionError, exceptions.Timeout]) 41 | 42 | # store the search term in a convenience variable then set the 43 | # headers and search parameters 44 | term = args["query"] 45 | headers = {"Ocp-Apim-Subscription-Key": API_KEY} 46 | params = {"q": term, "offset": 0, "count": GROUP_SIZE} 47 | 48 | # make the search 49 | print("[INFO] searching Bing API for '{}'".format(term)) 50 | search = requests.get(URL, headers=headers, params=params) 51 | search.raise_for_status() 52 | 53 | # grab the results from the search, including the total number of 54 | # estimated results returned by the Bing API 55 | results = search.json() 56 | estNumResults = min(results["totalEstimatedMatches"], MAX_RESULTS) 57 | print("[INFO] {} total results for '{}'".format(estNumResults, 58 | term)) 59 | 60 | # initialize the total number of images downloaded thus far 61 | total = 0 62 | 63 | # loop over the estimated number of results in `GROUP_SIZE` groups 64 | for offset in range(0, estNumResults, GROUP_SIZE): 65 | # update the search parameters using the current offset, then 66 | # make the request to fetch the results 67 | print("[INFO] making request for group {}-{} of {}...".format( 68 | offset, offset + GROUP_SIZE, estNumResults)) 69 | params["offset"] = offset 70 | search = requests.get(URL, headers=headers, params=params) 71 | search.raise_for_status() 72 | results = search.json() 73 | print("[INFO] saving images for group {}-{} of {}...".format( 74 | offset, offset + GROUP_SIZE, estNumResults)) 75 | 76 | # loop over the results 77 | for v in results["value"]: 78 | # try to download the image 79 | try: 80 | # make a request to download the image 81 | print("[INFO] fetching: {}".format(v["contentUrl"])) 82 | r = requests.get(v["contentUrl"], timeout=30) 83 | 84 | # build the path to the output image 85 | ext = v["contentUrl"][v["contentUrl"].rfind("."):] 86 | p = os.path.sep.join([args["output"], "{}{}".format( 87 | str(total).zfill(8), ext)]) 88 | 89 | # write the image to disk 90 | f = open(p, "wb") 91 | f.write(r.content) 92 | f.close() 93 | 94 | # catch any errors that would not unable us to download the 95 | # image 96 | except Exception as e: 97 | # check to see if our exception is in our list of 98 | # exceptions to check for 99 | if type(e) in EXCEPTIONS: 100 | print("[INFO] skipping: {}".format(v["contentUrl"])) 101 | continue 102 | 103 | # try to load the image from disk 104 | image = cv2.imread(p) 105 | 106 | # if the image is `None` then we could not properly load the 107 | # image from disk (so it should be ignored) 108 | if image is None: 109 | print("[INFO] deleting: {}".format(p)) 110 | os.remove(p) 111 | continue 112 | 113 | # update the counter 114 | total += 1 115 | -------------------------------------------------------------------------------- /Keras/multi-lable classification/train.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*-coding:utf-8 -*- 3 | """ 4 | @Author: Luocai 5 | @file: train.py 6 | @time: 2019/07/13 15:51 7 | @desc: 8 | 9 | 10 | """ 11 | # set the matplotlib backend so figures can be saved in the background 12 | import matplotlib 13 | 14 | matplotlib.use("Agg") 15 | 16 | # import the necessary packages 17 | from keras.preprocessing.image import ImageDataGenerator 18 | from keras.optimizers import Adam 19 | from keras.preprocessing.image import img_to_array 20 | from sklearn.preprocessing import MultiLabelBinarizer 21 | from sklearn.model_selection import train_test_split 22 | import matplotlib.pyplot as plt 23 | from imutils import paths 24 | import numpy as np 25 | import argparse 26 | import random 27 | import pickle 28 | import cv2 29 | import os 30 | 31 | from models.smallervggnet import SmallerVGGNet 32 | 33 | # construct the argument parse and parse the arguments 34 | ap = argparse.ArgumentParser() 35 | ap.add_argument("-d", "--dataset", required=True, 36 | help="path to input dataset (i.e., directory of images)") 37 | ap.add_argument("-m", "--model", required=True, 38 | help="path to output model") 39 | ap.add_argument("-l", "--labelbin", required=True, 40 | help="path to output label binarizer") 41 | ap.add_argument("-p", "--plot", type=str, default="plot.png", 42 | help="path to output accuracy/loss plot") 43 | args = vars(ap.parse_args()) 44 | 45 | # initialize the number of epochs to train for, initial learning rate, 46 | # batch size, and image dimensions 47 | EPOCHS = 75 48 | INIT_LR = 1e-3 49 | BS = 32 50 | IMAGE_DIMS = (96, 96, 3) 51 | 52 | # grab the image paths and randomly shuffle them 53 | print("[INFO] loading images...") 54 | imagePaths = sorted(list(paths.list_images(args["dataset"]))) 55 | random.seed(42) 56 | random.shuffle(imagePaths) 57 | 58 | # initialize the data and labels 59 | data = [] 60 | labels = [] 61 | 62 | # loop over the input images 63 | for imagePath in imagePaths: 64 | # load the image, pre-process it, and store it in the data list 65 | image = cv2.imread(imagePath) 66 | image = cv2.resize(image, (IMAGE_DIMS[1], IMAGE_DIMS[0])) 67 | image = img_to_array(image) 68 | data.append(image) 69 | 70 | # extract set of class labels from the image path and update the 71 | # labels list 72 | l = label = imagePath.split(os.path.sep)[-2].split("_") 73 | labels.append(l) 74 | 75 | # scale the raw pixel intensities to the range [0, 1] 76 | data = np.array(data, dtype="float") / 255.0 77 | labels = np.array(labels) 78 | print("[INFO] data matrix: {} images ({:.2f}MB)".format( 79 | len(imagePaths), data.nbytes / (1024 * 1000.0))) 80 | 81 | # binarize the labels using scikit-learn's special multi-label 82 | # binarizer implementation 83 | print("[INFO] class labels:") 84 | mlb = MultiLabelBinarizer() 85 | labels = mlb.fit_transform(labels) 86 | 87 | # loop over each of the possible class labels and show them 88 | for (i, label) in enumerate(mlb.classes_): 89 | print("{}. {}".format(i + 1, label)) 90 | 91 | # partition the data into training and testing splits using 80% of 92 | # the data for training and the remaining 20% for testing 93 | (trainX, testX, trainY, testY) = train_test_split(data, 94 | labels, test_size=0.2, random_state=42) 95 | 96 | # construct the image generator for data augmentation 97 | aug = ImageDataGenerator(rotation_range=25, width_shift_range=0.1, 98 | height_shift_range=0.1, shear_range=0.2, zoom_range=0.2, 99 | horizontal_flip=True, fill_mode="nearest") 100 | 101 | # initialize the model using a sigmoid activation as the final layer 102 | # in the network so we can perform multi-label classification 103 | print("[INFO] compiling model...") 104 | model = SmallerVGGNet.build( 105 | width=IMAGE_DIMS[1], height=IMAGE_DIMS[0], 106 | depth=IMAGE_DIMS[2], classes=len(mlb.classes_), 107 | finalAct="sigmoid") 108 | 109 | # initialize the optimizer 110 | opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS) 111 | 112 | # compile the model using binary cross-entropy rather than 113 | # categorical cross-entropy -- this may seem counterintuitive for 114 | # multi-label classification, but keep in mind that the goal here 115 | # is to treat each output label as an independent Bernoulli 116 | # distribution 117 | model.compile(loss="binary_crossentropy", optimizer=opt, 118 | metrics=["accuracy"]) 119 | 120 | # train the network 121 | print("[INFO] training network...") 122 | H = model.fit_generator( 123 | aug.flow(trainX, trainY, batch_size=BS), 124 | validation_data=(testX, testY), 125 | steps_per_epoch=len(trainX) // BS, 126 | epochs=EPOCHS, verbose=1) 127 | 128 | # save the model to disk 129 | print("[INFO] serializing network...") 130 | model.save(args["model"]) 131 | 132 | # save the multi-label binarizer to disk 133 | print("[INFO] serializing label binarizer...") 134 | f = open(args["labelbin"], "wb") 135 | f.write(pickle.dumps(mlb)) 136 | f.close() 137 | 138 | # plot the training loss and accuracy 139 | plt.style.use("ggplot") 140 | plt.figure() 141 | N = EPOCHS 142 | plt.plot(np.arange(0, N), H.history["loss"], label="train_loss") 143 | plt.plot(np.arange(0, N), H.history["val_loss"], label="val_loss") 144 | plt.plot(np.arange(0, N), H.history["acc"], label="train_acc") 145 | plt.plot(np.arange(0, N), H.history["val_acc"], label="val_acc") 146 | plt.title("Training Loss and Accuracy") 147 | plt.xlabel("Epoch #") 148 | plt.ylabel("Loss/Accuracy") 149 | plt.legend(loc="upper left") 150 | plt.savefig(args["plot"]) 151 | 152 | -------------------------------------------------------------------------------- /Notes/Deep Rectangling for Image Stitching.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccc013/DeepLearning_Notes/040201fb1654b01defdaf71db15f5a9bc1ca66fb/Notes/Deep Rectangling for Image Stitching.docx -------------------------------------------------------------------------------- /Notes/Depth_Aware_Multi-Grid.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccc013/DeepLearning_Notes/040201fb1654b01defdaf71db15f5a9bc1ca66fb/Notes/Depth_Aware_Multi-Grid.docx -------------------------------------------------------------------------------- /Notes/LeveragingLine-pointConsistenceNotes.md: -------------------------------------------------------------------------------- 1 | # Leveraging Line-point Consistence to Preserve Structures for Wide Parallax Image Stitching 2 | 3 | 论文链接:https://openaccess.thecvf.com/content/CVPR2021/papers/Jia_Leveraging_Line-Point_Consistence_To_Preserve_Structures_for_Wide_Parallax_Image_CVPR_2021_paper.pdf 4 | 5 | 代码链接:https://github.com/dut-media-lab/Image-Stitching 6 | 7 | 8 | 9 | ## 摘要 10 | 11 | 生成具有自然结构的高质量拼接图像是计算机视觉中的一项具有挑战性的任务。在本文中,我们成功地保留了宽视差图像的局部和全局几何结构,同时减少了伪影和失真。投影不变量特征数用于匹配输入图像的共面局部子区域。**这些匹配良好的子区域之间的单应性产生一致的线和点对,抑制重叠区域中的伪影**。我们探索并将全局共线结构引入到一个目标函数中,以指定和平衡图像变形所需的特征,**这可以在减轻失真的同时保留局部和全局结构**。考虑到人类视觉对线性结构的敏感性,我们还开发了综合的拼接质量的综合测量方法,来量化点的共线性和匹配线对的差异。大量实验通过呈现清晰的纹理并在拼接图像中保留突出的自然结构,证明了所提出的方法优于最先进的方法。特别是,我们的方法不仅表现出较低的错误,而且在所有测试图像中的差异最小。 12 | 13 | 14 | 15 | ------ 16 | 17 | ## 1.介绍 18 | 19 | ​ 图像拼接,将多个图像组合成具有更宽视野的更大图像[25],广泛用于摄影测量[24]、机器人导航[6]和智能手机全景[29]。为最先进的技术生成高质量的拼接图像仍然具有挑战性,因为它们会遭受严重不愉快的影响,例如伪影和失真,尤其是对于宽视差图像。 20 | 21 | ​ **特征匹配是对齐多个图像以产生无伪影拼接的关键**,因为匹配的特征充当对齐的锚。 SIFT 特征 [23] 广泛用于许多传统的特征点检测和匹配方法 [4, 5, 30]。最近的一些工作还**引入了线特征,以在点容易失配的大视差和/或低纹理的情况下获得稳健的匹配**[11]。林等人。在目标函数中通过不同的权重利用点和线特征[16]。不幸的是,这些方法分别匹配点和线,因此当不可避免地发生不匹配时,局部周围区域可能会不一致且不均匀地拉伸或压缩,从而在拼接图像中呈现伪影。廖等人。采用RANSAC策略通过使用图像之间的单应性来细化点和线对[17]。值得注意的是,单应关系仅适用于同一投影平面内的点和线[12]。因此,那些对单应性但忽略共面约束的改进无法给出准确的匹配。如图1中前三行红色矩形所示,右侧放大的重叠区域显示了相框、时钟和计算机上的伪影。探索共面区域并细化相应的点和线匹配对是非常可取的。 22 | 23 | 24 | 25 | ​ **图像拼接必须保留线性结构,同时减轻失真**,因为人类视觉感知对这些结构非常敏感。 as-projective-as-possible (APAP) 方法采用局部约束的参数扭曲 [30],但会遭受严重的形状扭曲,尤其是在非重叠区域,如图 1 中第一行的蓝色矩形所示。保形半投影 (SPHP) [4] 和全局相似性先验 (GSP) [5] 具有相似的想法,可以为不同的图像区域调整不同的扭曲。测地线保留[13]和线结构保留[3]涉及共线性保留,但它们的图像调整大小以一张全景图像作为输入,已经包含正确的全局几何结构作为参考。最近,廖等人提出单视角扭曲(SPW)[17]来保护线性结构,同时抑制扭曲。这些方法可以很好地保留局部结构,但**在同时维护局部和全局线性结构时无法解决冲突**。全局共线结构可以是横跨图像主要部分的一条长线,例如图 1 中两个图片帧下方的长线,也可以是几个单独的共线线段。当前的线检测器 [26] 无法检测或连接这些长线。因此,通过设置适当的参数可以很好地保留局部形状,但在图 1 的第二行中全局线性结构变形。在第三行中,保留线性结构会导致局部形状严重失真。保留局部和全局共线结构仍然没有解决。 26 | 27 | ​ 同时,现有评价拼接质量的指标还不够全面。这些指标包括匹配点之间的距离 [30] 和像素强度局部模式上的平均几何误差 (SSIM) [27] 只能量化点匹配的性能。它们都不能反映线性结构上点的对齐或匹配线段的共线性。定量评估图像拼接线性结构的保存也是一个悬而未决的问题。 28 | 29 | ​ 本文利用线和点的一致性来保留线性结构,这些线性结构是图像拼接的基本几何形状。我们将输入图像划分为线邻域上的共面区域,并使用一系列**反映线和点固有性质的几何不变量**来匹配来自不同视图的区域。因此,这些共面区域之间的单应性可以准确地生成线和点的匹配。随后,设计了用于warp的线引导目标函数以保留局部和全局线性结构并抑制失真。图 1 中的第四行表明我们的方法显着提高了图像质量。此外,为了更全面地分析拼接图像的质量,提出了一种线条的定量评价方法。我们的贡献总结如下: 30 | • 我们设计了一种新的匹配策略,通过使用射影不变量探索共面子区域来获得一致的点和线对。这种匹配遵循基本的共面要求用于单应性,以便它可以提供准确的预对齐,同时消除伪影和非均匀失真。 31 | • 据我们所知,我们是第一个将全局共线结构合并为显着减轻非自然扭曲的约束。 32 | • 我们提出了一个综合度量来量化图像拼接的线性结构的保存。 33 | 34 | ​ 我们将提出的方法与最先进的具有挑战性的自然图像对进行比较,具有突出的线性结构,涵盖相机运动、场景和视野的变化。我们的方法可以产生视觉上吸引人的拼接,我们的点匹配平均 RMSE 比 SPW [17] 低 31%。同时,我们的方法最准确、最稳定,可以根据所提出的度量来保留线性结构。第 3、4 和 5 节分别阐述了我们的贡献。 35 | 36 | 37 | 38 | ## 2. 相关工作 39 | 40 | ​ 本文提出了同时保留局部和全局结构的线引导图像拼接方法。因此,本节回顾了以前与扭曲相关的工作,以减少失真和具有线结构约束的扭曲。 41 | 42 | ​ 传统的拼接方法通常为每个输入图像估计一个最优的全局变换。它们仅适用于理想的近平面场景,并且生成的图像通常会受到局部伪影和投影失真的影响 [2]。因此,一些方法试图使扭曲适应图像的不同区域。林等人[21]提出了一种平滑变化的仿射(SVA)变换,以实现更好的局部适应。李等人使用贝叶斯模型去除异常值和用于分析扭曲的薄板样条[14]。高等人将图像分为地平面和远平面,并提出双单应扭曲(DHW)[8]来减少失真。保形半投影(SPHP)扭曲[4]结合了重叠和非重叠区域的投影变换。 Adaptive as-natural-as-possible (AANAP) warps [18] 有类似的想法,将重叠区域的单应变换转换为整个图像。赫尔曼等人[9]引入多个配准以获取更高的准确性,而不是单个配准。李等人。提出了一种准单应性(QH)扭曲[15],它依赖于全局单应性,同时挤压非重叠区域。但是,它们不够灵活,无法减少视差较大的场景的失真。 43 | 44 | ​ 为了获得更好的对齐和更少的失真,APAP [30] 微调全局单应性扭曲以适应位置相关的对齐。陈等人通过最小化由对齐、局部和全局相似性项组成的能量函数,提出了一种基于全局相似性先验 (GSP) 的扭曲 [5]。他们的方法旨在解决非重叠区域的失真,但线性结构没有得到很好的保护。张等人通过设置一系列先验约束和手动指导来获得更好的性能[32]。林等人考虑到像素强度的差异,这在低纹理图像中效果很好[20]。李等人将图像分割成超像素,并根据视差场景的翘曲残差使用计算出的特征匹配来自适应地翘曲它们[12]。 45 | 46 | 此外,还有一些基于接缝的方法可以减少局部失真。提出了一种容忍视差的扭曲,它结合了单应性和内容保留扭曲(CPW)[22]来控制失真[31]。然而,他们的方法仍然会导致大视差的形状失真。Lin 等人。通过自适应特征加权迭代地改进接缝引导的局部对齐,并引入了一个新术语来保留显着线结构方法[19]。然而,非重叠区域仍然存在全局失真。 47 | 48 | 为了实现更好的拼接质量,减少失真并保留线性结构,Li 等人将线特征引入图像拼接,通过引入线性对齐项[16]来改进内容保留扭曲。向等人提出了一种具有全局相似性约束的线引导局部扭曲[28]。廖等人同时强调单视角扭曲的不同特征,包括对齐、扭曲和显着性 [17]。然而,全局共线结构很少被解决,并且在这些方法中仍然存在局部和全局结构保持之间的冲突。 49 | 50 | 51 | 52 | ## 3. 基于点线约束一致性进行预对齐 53 | 54 | 在本节中,设计了一种基于双特征(线和点)的预对齐算法,如图 2 所示。 55 | 56 | - 首先,基于在线检测将图像划分为共面子区域,其中一个如图 2 所示 的绿色矩形。 57 | - 然后,通过从一系列射影不变量计算的相似度来匹配子区域。 58 | - 第三,增加和细化匹配点对,通过匹配区域之间的单应性匹配线。 59 | - 最后,基于双重特征构建全局预对齐。 60 | 61 | ![](../images/LeveragingLine-pointConsistenceNotes/Fig2.png) 62 | 63 | ### 3.1 基于线检测的子区域划分 64 | 65 | ​ **共面区域之间的局部单应性比全局单应性更准确**。 由于多条线是由平面相交形成的,我们粗略假设,**由线长确定的邻域可以看作是图像的局部共面子区域**。利用LSD[26]得到原图线段,然后根据梯度方向将线的邻域分为左侧和右侧,因为位于线不同侧的点可能不共面。 **一条线的梯度定义为它上面所有点的平均梯度**。 如图 3 所示,在一条直线的附近,任何一个像素点到直线的距离都小于 α·len(l),并且到垂直平分线的距离小于 β·len(l)。 在我们的实验中,α 和 β 分别设置为 2.0 和 0.5 [10]。 66 | 67 | ![](../images/LeveragingLine-pointConsistenceNotes/Fig3.png) 68 | 69 | ### 3.2 基于点线不变性的子区域匹配 70 | 71 | ​ 为了匹配共面的子区域,最终匹配更多的线点对,引入投射不变特征数(CN)来构造线-点不变量,并在此基础上定义子区域之间的相似度。 CN 定义如下: 72 | 73 | ​ 令 K 为域,$P^m$(K) 为 K 上的 m 维投影空间,$\{P_i\}_{i=1,2,...,R}$ 为 $P^m$(K) 中构造闭环的不同点( $P_{R+1}=P_1$)。 在线段 $\{P_iP_{i+1} \}_{i=1,2,...,R}$ 上有不同的点$\{Q^{(j)}\}_{i=1,2,...,S}$ ,每个点 $Q_i^{(j)}$ 可以由 $P_i$ 和 $P_{i+1}$ 线性表示为 $Q_i^{(j)}=a_i^{(j)}P_i+b_i^{(j)}P_{i+1}$。 令 $P=\{P_i\}_{i=1,2,...,R},Q=\{Q_i^{(j)}\}_{i=1,2,...,R}^{j=1,2,...,S}$,则该量称为 P 和 Q 的特征数 74 | 75 | ![](../images/LeveragingLine-pointConsistenceNotes/eq1.png) 76 | 77 | ​ **由于 CN 的构造需要一个闭环并且每条边上的点数相等,因此我们使用五个点来构造一个三角形,并且每条边上都有相等的交点** [10]。 如图2左上图所示,$K_l^1$ 和 $K_l^2$ 是红线 l 上的两个端点。 $P_1, P_2, P_3$ 是直线同一侧的三个非共线特征点,用红点标记。 任意三个点($K_l^1, K_l^2, P_1, P_2, P_3$)不共线。 78 | 79 | ​ 我们用两个点 $P_i, P_j$ 表示一条直线,即 $\overline{P_iP_j}$,然后两条直线 $\overline{P_iP_j}, \overline{P_kP_m}$ 的交点表示为 $<\overline{P_iP_j}, \overline{P_kP_m}>$。我们可以获取几个相交点(蓝色点),包括 $U_1=<\overline{K^1_lP_1}, \overline{K_l^2P_3}>, U_2=<\overline{K^1_lP_1}, \overline{P_2P_3}>, U_3=<\overline{P_1P_2}, \overline{K_l^2P_3}>, U_4=<\overline{K^1_lP_3}, \overline{P_1K_l^2}>, U_5=<\overline{K^1_lK_l^2}, \overline{U_1P_2}>, U_6=<\overline{K^1_lK_l^2}, \overline{U_1U_4}>$. 80 | 81 | ​ 因此,我们有 $\triangle K_l^1U_1K_l^2$,然后可以计算 $P=\{K_l^2,U_1, K_l^2\},Q=\{P_1, U_2, U_3, P_3, U_5, U_6\}$的 CN。此后,在图2左下图所示的另一个视图中,我们可以用同样的方法构造 $\triangle K_l^{1'}U_1^'K_l^{2'}$,$CN(l, P_1, P_2,P_3)$等于$CN(l' , P'_1, P'_2,P'_3) $具有相应的匹配线和点对。 通过不同的特征点可以得到一系列的CN值。 82 | 83 | ​ 令 $I 和 I'$ 分别表示目标图像和参考图像。 我们使用 SURF [1] 来检测和匹配特征,并使用 LSD [26] 来检测线条。 然后,我们可以根据相应区域内的一系列 CN 值计算候选子区域之间的相似度 [10]。 对于相似度最高的匹配子区域 $Reg ∈ I 和 Reg' ∈ I'$,使用匹配区域内现有的匹配点对构建 CN,如图 2 左图所示。$\triangle K^1_l U_1 K^2_l$ 上的 $U_1, U_2、U_3,U_4,U_5,U_6$,以及 $\triangle K^{1′}_l U^′_1K_l^{2'}$上的对应点$U^′_1、U^′_2、U^′_3、U^′_4、U^′_5、U^′_6'$ 被添加到匹配点集中以增加用于翘曲的锚点。 最后,我们使用 RANSAC [7] 来细化匹配点并估计其局部单应性 H,用于在子区域 [10] 中获得更多匹配的线。 84 | 85 | 86 | 87 | ### 3.3 基于双特征的预对齐 88 | 89 | 设 $p_i = (x_i, y_i, 1) 和 p^\prime_i = (x^\prime_i, y^\prime_i , 1) $为匹配点对 $\{(p_i, p^\prime_i)\}_{i=1,2,...,N}$在齐次坐标中,其中 N 是匹配点对的数量,对于匹配线对集合 $\{(l_j , l^\prime_j )\}_{j=1,2,...,L}, l_j ∈ I 和 l^\prime_ j ∈ I^\prime$,其中 L 是匹配线对的数量。 线 $l_j$ 表示为$ (l^s_j, l^e_j)$,其中 $l^s_j 和 l^e_j$ 是两个端点。 为了获得更好的配准,应该最小化扭曲后匹配点和线之间的欧几里得距离。 我们将 H 表示为初始单应性,$H_∗$ 是 H 的向量表达式,$\hat H_*$ 是期望的单应性。 因此,基于对偶特征的全局单应性可以表示为 90 | 91 | ![](../images/LeveragingLine-pointConsistenceNotes/eq2.png) 92 | 93 | 其中 $dis(l^\prime_j,HL_j^{s,e})$ 表示终点 $HL_j^{s,e}$ 和线 $l^\prime_j, H_* \in R^9$。$U_i \in R^{2\times 9}, V_j\in r^{2\times 9}$ 。通过 SVD 可以很容易最小化函数 $[U_i, V_j]^T H_*=0$。此外,我们使用归一化和坐标轴旋转来提高模型 [4] 和 [17] 的稳定性和准确性。 94 | 95 | 96 | 97 | ## 4. 全局线指导的网格变形 98 | 99 | ​ **预对齐估计的全局单应性只提供了一个近似的变换,但仍然存在失真和弯线。 凸线越长,直线弯曲越大**。 为了解决这个问题,我们探索了线保持约束的全局共线结构,并将其与点线对齐和能量函数中的失真项结合起来。 100 | 101 | ### 4.1 能量函数定义 102 | 103 | 首先,我们为每个图像对构建矩形网格。 令 I 和 I ' 分别表示目标和参考图像。 假设网格网格顶点的索引是从 1 到 n。 向量 $V = [x_1 y_1 x_2 y_2 ···x_n y_n]^T (V ∈ R^{2n}) $ 用于描述原始顶点的坐标,向量 $\hat V = [\hat x_1 \hat y_1 \hat x_2 \hat y_2··· \hat x_n \hat y_n]^T (\hat V ∈ R^{2n} ) $表示扭曲顶点的坐标。 对于任何样本点 p ∈ I,我们通过其四个封闭网格顶点的双线性插值将其表示为 τ (p)。 然后,对应的翘曲点pb表示为 $τ(\hat p)$。 总能量函数 $E(\hat V)$ 为 104 | 105 | ![](../images/LeveragingLine-pointConsistenceNotes/eq3.png) 106 | 107 | 其中 $E_{lp}(\hat V)$ 是通过保持局部和全局的线的线保留项,$E_a(\hat V)$ 是通过改进匹配点线的对应关系的点线对齐项,而$E_d{\hat V}$ 是通过保留网格线的斜率和均匀 warp 相邻网格的失真控制项。 108 | 109 | ### 4.2 线保留项 110 | 111 | 局部单个显着线和全局共线线段对于扭曲都是至关重要的。 因此,行保留项定义为: 112 | 113 | ![](../images/LeveragingLine-pointConsistenceNotes/eq4.png) 114 | 115 | 如图 4 所示,局部红线 $l_1, . . , l_5$ 在空间上是分开的,但它们是共线的,如全局蓝线 $l_6$ 所示。如图 4 中的第二幅图像所示,可以保留局部线结构,但它们的共线性在翘曲过程中很容易被破坏。由于这种失真对人类对图像质量的感知非常令人不安,**我们设计了一种局部扭曲的合并策略,通过评估线段的共线性来保持全局线性结构,这是我们的主要贡献之一**。合并过程在算法 1 中有详细说明。我们每次都评估成对的线,合并的线应该满足三个约束: 116 | 117 | 1. 首先,两条线$slope(l_i)$和 $slope(l_j)$ 的斜率应该接近。 118 | 119 | 2. 其次,从端点到另一条线的距离,即 $dis(l_j , p^e_{l_i} ) 和 dis(l_j , p^s_{l_i})$ 应该很小。 120 | 121 | 3. 第三,两条线的相邻端点 $dis(p^e_{l_i}, p^s_{l_j})$ 的距离应该很小,如图 4 所示。 122 | 123 | 注意我们引入了 flag 以避免合并线的无限循环,最初设置为 0 和合并后设置为1。虽然局部线最有可能通过原始线拟合获得,但全局线通常来自合并过程。值得注意的是,每条线的长度 len (l) 用于通过阈值 µ 对局部和全局线段进行分类。 124 | 125 | ![](../images/LeveragingLine-pointConsistenceNotes/Fig4.png) 126 | 127 | 这里我们从全局线集合$S_{gl}$ 中的直线 $\{l_g\}_{g=1,2,...,Q}$ 作为一个例子,其中 Q 表示线的数量。每条线都是均匀采样了$M_g$ 个点 $\{p_k^g\}_{k=1,2,...,M_g}^{g=1,2,...,Q}$,则: 128 | 129 | ![](../images/LeveragingLine-pointConsistenceNotes/eq5.png) 130 | 131 | 其中 $\overrightarrow n^g$ 表示 $l_g$ 的归一化向量,$W_{gl} \in R^{(\sum_{g=1}^Q(M_g-1))\times 2n}$ 。我们采用相同的方法来构建对局部线段的约束项。 132 | 133 | ![](../images/LeveragingLine-pointConsistenceNotes/algo1.png) 134 | 135 | ### 4.3 点线对齐和失真控制 136 | 137 | 点-线对齐项是基于一个直观的约束来定义的,即匹配的点和线对在扭曲后应该相互重合,定义为 138 | 139 | ![](../images/LeveragingLine-pointConsistenceNotes/eq6.png) 140 | 141 | $E_p(\hat V) 和 E_l(\hat V)$ 分别是点和线的对齐项。 142 | 143 | 为了控制目标图像I的畸变,构造了一系列水平和垂直线,称为交叉线。 这些构造的线被认为是图像 I 的固有线性结构,如图 5 中的红线所示。线的斜率和它们交叉点之间的空间用于控制失真。 失真项由全局项 Edg 和非重叠项 Edn 定义 144 | 145 | ![](../images/LeveragingLine-pointConsistenceNotes/eq7.png) 146 | 147 | 由于所有约束项都是二次的,因此可以通过**稀疏线性求解器对其进行重新公式化和最小化**。 更多细节可以在[17]中找到。 翘曲结果与 SPW [17] 进行比较,后者也具有线约束。 两种方法对于原始目标图像 I 具有相同数量的均匀间隔交叉线。 翘曲结果如图 5 所示,我们的方法表现出密集的交叉线和从重叠区域到非重叠区域的适度过渡,表现出对失真的良好控制,而 SPW [17] 的结果中出现了明显的失真,如图放大的矩形。 148 | 149 | ![](../images/LeveragingLine-pointConsistenceNotes/Fig5.png) 150 | 151 | ## 5. 共线性的定量评价 152 | 153 | 为了量化线性结构的拼接性能,我们设计了一种新的评估方法,该方法考虑了三个方面: 154 | 155 | - 点的共线性 156 | - 匹配线的距离 157 | - 匹配线方向的差异。 158 | 159 | 我们在 $l_j$ 线上统一采样 $P_s$ 个点 $\{p^j_k\}_{k=1,2,...,P_s}$,在图 6 中用红色标记。图 6 左下图展示了拟合线 $l^π_j = π(\{\hat d^j_k\}_{k=1,2,...,P_s} ) $通过最小二乘法得到扭曲点 $\{\hat d^j_k\}_{k=1,2,...,P_s}$。 对于 L 条线的误差项 $E_{err}$ 定义为: 160 | 161 | ![](../images/LeveragingLine-pointConsistenceNotes/eq8.png) 162 | 163 | 其中 $\hat p^j_k(y)$ 表示 $\hat p_k^j$ 的 y 坐标,而 $l_j^\pi(y)_{x=\hat p^j_k(x)}$ 表示和 $\hat p_k^j$拥有相同 x 坐标的 $l_j^\pi$ 的 y 坐标。 164 | 165 | 正如图 6 右上图所示,距离项 $E_{dis}$ 表示两个匹配的终点和匹配线段的距离,定义如下: 166 | 167 | ![](../images/LeveragingLine-pointConsistenceNotes/eq9.png) 168 | 169 | 其中 $\hat p_1^j,\hat p_{P_s}^j$ 表示 $\hat l_j$ 的两个终点。 170 | 171 | ![](../images/LeveragingLine-pointConsistenceNotes/Fig6.png) 172 | 173 | 方向项 $E_{dir}$ 估计扭曲线和匹配线之间的方向差异。我们分别用 $\overrightarrow{\hat l_j}, \overrightarrow{l_j^\prime}$ 表示线 $\hat l_j, l_j^\prime$ 的方向向量。具有较小 x 坐标的端点用作每个向量的起点。 如图 6 右下图所示,两条线的叉积 $\overrightarrow{l_j^\prime}\times \overrightarrow{l_j }=len(l_j^\prime)\times len(\hat l_j)\times sin(\theta)$ 可以反映两个向量的方向差异,也取 $len(l_j^\prime)和 len(\hat l_j ) $两行的长度,因此,对于所有直线的 $E_{dir}$ 表示为: 174 | 175 | ![](../images/LeveragingLine-pointConsistenceNotes/eq10.png) 176 | 177 | ## 6. 实验 178 | 179 | 我们通过消融研究证明了所提出方法的有效性,并在 15 个测试图像对上与最新技术进行了定量和定性比较,这些图像对涵盖了不同类型的数据集,涉及相机运动、场景和视场 . 输入图像对的大小调整为 1000 × 800 像素,每个网格的大小为 40 × 40。这使我们能够保持所有参数不变。 划分局部和全局线段的阈值 µ 设置为网格对角线长度的三倍。 在能量函数中,$λ_{lo} 和 λ_{gl}$ 设置为 50 和 100 以保持线,$λ_p 和 λ_l$ 设置为 1 和 5 用于点线对齐,$λ_{dg} 和 λ_{dn}$ 设置为 50 和 100 以最小化失真。 180 | 181 | ### 6.1 消融实验 182 | 183 | **一致的线和点约束**可以提供**准确的对齐并抑制伪影**。我们用单独的点和线匹配策略[17]代替我们的联合匹配策略,其他部分保持不变。示例结果如图 8 所示。共有三个拼接实例,放大的重叠区域显示在每个结果的右侧。正如我们所看到的,我们的关节点和线匹配在图 8(b)中产生了清晰的拼接结果,而在图 8(a)中存在明显的伪影。 184 | 185 | ![](../images/LeveragingLine-pointConsistenceNotes/Fig8.png) 186 | 187 | **线性结构保持**可以保持局部和全局线性结构并抑制失真,如图9所示。在图9(a)中,局部形状得到了很好的保留,但线条在没有线性约束的情况下弯曲。当我们粗略地使用 LSD 检测到的线时,建筑物和地面上的短线要好得多,但仍然不够直。特别是,自行车在图 9(b)中有严重的变形。相比之下,局部和全局线性约束在我们的方法中得到了很好的平衡,如图 9(c) 所示。 188 | 189 | ![](../images/LeveragingLine-pointConsistenceNotes/Fig9.png) 190 | 191 | ### 6.2 对比 state-of-the-arts 192 | 193 | 进行了几个实验以将拼接结果与现有方法进行比较,包括 SVA [21]、CPW [22]、APAP [30]、ELA [14]、SPHP [4]、GSP [5] 和 SPW [17] .定量和定性结果来自他们的论文或发布的代码。 194 | 195 | 我们对点和线都采用定量评价。点的对齐精度由一组匹配点上的均方根误差 (RMSE) [30] 来衡量。表 1 描述了第一列中命名的 7 个图像对的 RMSE 值。我们的方法在所有 7 对上产生了最低的错误。我们的平均误差为 1.7014,比 SPW 低 31%。此外,我们的误差方差为 0.05,比 SPW 的值 0.75 低 93%。 196 | 197 | ![](../images/LeveragingLine-pointConsistenceNotes/tab1.png) 198 | 199 | 此外,我们使用我们在第五节中提出的三个措施来评估线性结构。 在图 7 中,我们将我们的方法与 SPW [17] 进行比较,后者具有局部但没有全局线约束。如图 7 所示,x 坐标表示图像对的名称,垂直坐标表示误差。我们的方法在共线性和角度差方面优于 SPW,如图 7(a) 和图 7(c) 所示。在图 7(b) 中,我们的误差方差为 0.0855,比 SPW 的 0.6630 小 87.11%,表明所有对的发散误差都较小。在图像对“学校”和“建筑”上,SPW 发生了严重变化,我们的错误大约是我们的两倍。 200 | 201 | ![](../images/LeveragingLine-pointConsistenceNotes/Fig7.png) 202 | 203 | 图 10 展示了全面的视觉比较。我们的方法在保留线性结构和产生清晰且无伪影的重叠区域方面优于所有其他方法。 前四行的线性结构都表现出严重的弯曲,用红色标记。 在放大的矩形中显示的现有方法的结果中出现了明显的伪影,例如花朵、桌子和遮阳伞。 204 | 205 | ![](../images/LeveragingLine-pointConsistenceNotes/Fig10.png) 206 | 207 | ## 7.结论 208 | 209 | 我们提出了一种基于线引导变形和线点约束的结构保持图像拼接方法。 我们将输入图像划分为子区域,并通过线点不变量进行匹配。 局部匹配为预对齐提供准确的线和点对,在重叠区域没有显示模糊或伪影。 我们提出了一种线引导扭曲来保留局部和全局结构,同时消除非重叠区域的失真。 此外,我们为线性结构设计了一种新的定量评估方法,这与人类的感知一致,因为人类视觉对线性结构的扭曲非常敏感。 实验结果表明,所提出的方法在具有挑战性的测试图像上准确地对齐重叠区域和非重叠区域,并且与现有技术相比产生了明显更好的性能。 210 | 211 | 212 | 213 | -------------------------------------------------------------------------------- /Notes/LeveragingLine-pointConsistenceNotes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccc013/DeepLearning_Notes/040201fb1654b01defdaf71db15f5a9bc1ca66fb/Notes/LeveragingLine-pointConsistenceNotes.pdf -------------------------------------------------------------------------------- /Notes/NeuralRenderingnNotes.md: -------------------------------------------------------------------------------- 1 | # 神经渲染笔记 2 | 3 | ## 定义 4 | 5 | 渲染通常是指将 3D 场景,转变为 2D 图像的过程。 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | ------ 16 | 17 | ## 可微分渲染(Differentiable Rendering) 18 | 19 | 简单的定义: 20 | 21 | > 可微分渲染是指可以对各类三维表示(比如Mesh、Voxel)进行合理渲染,并能回传渲染颜色关于渲染参数的梯度的一类梯度方法,简单来说就是**可微分渲染就是计算渲染过程的导数**。 22 | 23 | 普通的渲染过程是将 3D 场景扔进渲染器得到 2D 图像,其逆向过程,称为逆向渲染(Inverse Rendering),就是从 2D 图像得到获得生成这张图像所需要的 3D 信息,它们可以是 3D 几何、光照、材质、视角等其中的一种或者多种。 24 | 25 | 如下图展示了一个渲染和逆向渲染过程,图片来自 Mitsuba2 论文: 26 | 27 | ![来自 Mitsuba2](https://pica.zhimg.com/80/v2-5b181bba9ccbf5d0b3e6048f10736903_720w.jpg?source=1940ef5c) 28 | 29 | 整个过程包括如下步骤: 30 | 31 | 1. 定义一个目标图片 A,目标是找到能生成这张图像的 3D 场景 B; 32 | 2. 首先是会先做一个初始的 3D 场景 B1,这个场景不要求很准确,错的或者大致是相似的都行; 33 | 3. 将 B1 送到渲染器中,渲染得到图片 A1 34 | 4. 和神经网络类似,比较 A 和 A1 的区别,然后计算 Loss 35 | 5. 将 Loss 反向传播到场景 B1 的不同参数上,比如材质、光照、相机参数等,修改这些参数的值,得到新的场景 B2 36 | 6. 和训练神经网络一样,重复步骤 3-5 这个过程,直到满足某个终止条件,比如迭代次数 37 | 38 | 其中第 5 步就是可微分渲染的关键部分,这一步需要计算梯度,从传统角度来看: 39 | 40 | 1. 渲染的计算过程是 Monto Carlo Ray Tracing,光线在整个场景中跳来跳去,这个计算过程中很多数值是离散的,并不太好计算出合适的梯度,目前一个解决方法是采用 Edge Sampling; 41 | 2. 渲染中有很多效果,包括 BSDF 函数,这个是一个比较新的领域,一个解决方法是 Mitsuba2 42 | 43 | 44 | 45 | 另外,实际上渲染过程包含了不同的模型,比如 pipeline rendering(实时渲染)和 physics-based rendering (离线渲染),因此其求导过程也差别很多,前者在视觉领域已经有一些实际的应用,主要是用光栅化的方法,典型的代表是Soft rasterizer(Soft Rasterizer: A Differentiable Renderer for Image-based 3D Reasoning)。后者主要是用光线追踪的方法,但由于是基于Monte Carlo,模型本身相对复杂一些,针对其求导的研究一直没有太大的进展,其难点包括: 46 | 47 | - 与visibility相关的导数(比如Geometry)难以计算 48 | - 如何快速的计算导数。 49 | 50 | 对于第一个问题,Tzumao的论文 (Differentiable Monte Carlo Ray Tracing through Edge Sampling)是第一篇针对visibility的论文,并通过 edge sampling 来解决,但是它的局限性在于,为了正确的计算visility相关的导数,需要在渲染过程中实时的进行 silhouette detection,使得计算会很慢。之后的两年陆续有若干篇physics-based differentiable rendering的论文。包括 51 | 52 | - Wenzel 的Reparameterization,提出了一种biased but fast的近似解法,不再需要edge sampling (Reparameterizing discontinuous integrands for differentiable rendering) 53 | - DTRT, 将可微渲染拓展到体渲染 ( A Differential Theory of Radiative Transfer) 54 | - PSDR, 将可微渲染拓展到Path Space, 并且不需要做 silhouette detection,因此对于performance也有很大的提高 (Path-Space Differentiable Rendering) 55 | - MIT 组的论文 (Unbiased Warped-Area Sampling for Differentiable Rendering),提出了一种新的算法,不需要做edge sampling, 理论上也是unbiased。 56 | 57 | 对于第二个问题,目前的情况是: 58 | 59 | 1. Mitsuba2是目前最快速(但与 Tzumao 的 redner 差别不大) 的 physics-based differentiable renderer ,但是对于GPU memory需求极大(Mitsuba 2: A Retargetable Forward and Inverse Renderer) 60 | 2. 为了解决GPU Memory的问题,Wenzel 组提出了Radiative Backpropagation (Radiative Backpropagation: An Adjoint Method for Lightning-Fast Differentiable Rendering) 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | ------ 72 | 73 | ### 基于网格的可微渲染 74 | 75 | 网格模型的传统渲染流程大致分为如下两步: 76 | - 为每个图片像素指定三角面片(光栅化) 77 | - 根据像素对应的三角面片与光照、纹理等渲染参数,计算像素的颜色; 78 | 79 | 80 | 81 | 许多应用中,网格的几何也是优化对象,由于**光栅化本身是关于网格几何和像素位置的不连续函数,这导致了传统的网格渲染是不可微的**。 82 | 83 | 84 | 85 | 目前基于网格的可微渲染解决该问题的方法分两种: 86 | - 保持渲染过程不变,近似梯度,典型的方法包括 OpenDR 87 | - 修改渲染管道(pipeline),绕过不可微的光栅化,比如 SoftRas 88 | 89 | 90 | 91 | ### 基于点云的可微渲染 92 | 93 | 传统点云渲染分为以下几步: 94 | - 将点云转到相机系 95 | - 计算每个点的颜色与影响范围; 96 | - 对每个像素,根据点云的深度计算像素颜色 97 | 98 | 99 | 100 | 在许多实际应用中,会将点云转为高斯分布,扩大单个点的影响范围,此时点云的渲染和可微化和 SoftRas 的思想类似 101 | 102 | 基于网格的可微渲染和基于点云的可微渲染有相似性:**两种几何表示都有广泛的应用,且渲染过程中都需要为像素指定着色图元**; 103 | 104 | 应用点云的可微渲染时,如同 SoftRas一文,**渲染精度和点云影响区域大小是需要平衡的变量**; 105 | 106 | 点云可微渲染的一个额外应用是深度图的新视角渲染,该渲染方法允许单目深度估计的自监督; 107 | 108 | 109 | 110 | ### 基于体素的可微渲染 111 | 112 | 三维物体的体素表示是一系列以体素形式存储的三维表示,比如: 113 | 114 | - Occupancy field,可以是 0/1 值,也可以是连续的浮点数; 115 | - SDF,表示空间中任一点距离三维物体表面的有向距离 116 | 117 | 体素表示的渲染过程如下: 118 | 119 | 1. 确定每条光线经过哪些体素; 120 | 2. 选取或者融合每条光线上体素的信息,得到渲染结果 121 | 122 | 通常来说,**体素表示的可微渲染设置只要求对第二部分进行可微化,此时,光线颜色信息来自一些固定位置的体素,不存在网格渲染那样的图元位移问题**。 123 | 124 | 125 | 126 | 使用体素作为物体的三维表示的最大问题是在于**存储空间,存储空间的增加速度是分辨率的三次方**,在要求高精度渲染的应用中是不合适的。目前的解决方法有这几个方向: 127 | 128 | 1. 用八叉树等数据结构减小体素的空间需求; 129 | 2. 直接使用隐式表示来替代体素表示; 130 | 131 | 132 | 133 | ### 基于隐式表示的可微渲染 134 | 135 | 隐式表示和体素表示一样,都是一类存储方式特定的三维表示的统称,比较流行的隐式表示包括 136 | 137 | - occupancy field 138 | - SDF 139 | - NeRF 140 | 141 | 隐式表示的可微渲染和体素表示的可微渲染有非常多相似之处,**其可微化大多数限于对固定点信息的优化,两者在渲染流程上的不同在于采样策略**。 142 | 143 | 144 | 145 | 隐式表示的优劣势: 146 | 147 | - 隐式表示本身是三维信息的利器,它可以在较小的体积内达成高分辨率甚至 photo-realistic 的渲染,相对网格和体素有明显的优势; 148 | - 基于隐式表示的可微渲染某种程度上已经成为了隐式表示的一部分,没有隐式表示的可微渲染,隐式表示的构造程度将会上升; 149 | - 最大的劣势在于**渲染速度较慢**,其根源在于**隐式表示的渲染需要对表示网格进行密集求值**。 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | ------ 160 | 161 | ## 数据 162 | 163 | ### OBJ 网格模型 164 | 165 | #### 实体建模 vs 表明建模 166 | 167 | 在说OBJ模型之前,先从大的写起,说说关于3D建模的分类。根据情况可以略过不看。 168 | 169 | > 所有的3D cad软件从机制上来说其实可以分三类: 170 | > 171 | > - 实体建模(Solid Modeling) 172 | > - 表面建模(Surface Modeling) 173 | > - 实体建模+表面建模。 174 | > 175 | > 即实际上建模方式只有两种:实体建模和表面建模。工程师用的软件偏向于实体建模,而艺术生用的软件偏向于表面建模。 176 | 177 | 实体建模: 178 | 179 | > 实体建模的工具方式基本上有三种:扫掠,立体布尔运算和表面围成。 180 | > 181 | > 1. **扫掠**通常指的是将一个平面沿着一条指定曲线做扫掠操作,然后这个平面扫过的空间就成为了目标固体(所谓的平面拉伸,旋转,方样等都属于这个种类)。 182 | > 2. 布尔运算是对两个固体来求和,求差或者求交。这里指的固体运算其实是指空间的运算。通常计算机都用一种被称为边界限定法来描述一个空间,布尔运算的实质是对这个被限定住的空间做逻辑运算。求和,指的是不同立体空间相加后构成包含了两个空间的新空间。类似的,求差指的是用一段空间去剪切另外一段空间等。 183 | > 3. **表面围成**就比较直接了,这种方法直接通过几个特殊的表面来生成固体,这些表面必须能够围住一个封闭的空间。 184 | 185 | 表面建模: 186 | 187 | > 表面建模可以用一句话概括:**添加或者修改点,让这些点能够约束一个表面**。 188 | > 表面建模只关注表面的外形,不关注这些表面是否能够围成一个封闭的空间。表面建模的整个过程都像做一个泥娃娃,你可以给它添加泥巴,也可以拿下一些泥巴,还可以捏这些泥巴。 189 | > 190 | > 而maya这些软件就是给你提供各种工具来玩这个泥娃娃。同样布尔操作也可以用在表面建模中,但是形成的意义有所不同。实体建模中的布尔运算是真正的立体空间求和,求差,求交;而表面模型的布尔操作就像是做不同的表面缝合操作。(通常3D打印用的stl格式基本上就是表面模型描述文件) 191 | 192 | 193 | 194 | OBJ 模型是属于表明建模模型的,然后它本身是ASCII格式,是可读的。 195 | 196 | **OBJ 模型的优点**: 197 | 198 | 1. 文件格式被广泛支持; 199 | 2. 3D打印机只识别STL或OBJ格式。 200 | 201 | 202 | 203 | #### 网格模型 204 | 205 | 表面建模模型中,两个点组成一条边,多条边组成一个面,多个面组成一个体,一个或多个体组成一个模型。可以说一个模型是由若干个面组成的。 206 | 207 | 在计算机图形学中,考虑到图形求交等计算的问题,这个面通常选择三角形,即模型的表面由若干三角面构成。一个模型文件需要记录的信息就是这些**三角面的信息**,进一步讲,一个三角面又需要有顶点、法线、连接顺序等这些信息。 208 | 209 | ##### 法线 210 | 211 | 顶点位置信息——x,y,z坐标值——描述了三角面的位置,但光有位置信息还不够,我们还需要知道一个三角面的正面与背面,为什么要这样,直观的理解就是,由于模型体只有表面,内部是空腔(如果模型是封闭的话),内部的面是不可见的,然后背面朝着我们(摄像机)的面也是不可见的。那怎么标注这个正面与背面呢?就是靠法线了。 212 | 213 | (补充:一个简单的例子就是,比如我们有一个立方体模型,这个立方体表示一个魔方之类的物品,这时它可见的是外表面,但如果它表示一间房间,人在房间里面观察,那个此时它可见的应该是内表面。) 214 | 215 | 所谓**面的法线就是垂直于此面的线,用一个向量表示,向量指向的一侧为正面,另一侧为反面。进一步地说,法线用处还是在光照模型中确定一个面的颜色**。 216 | 217 | 下图展示了一个无顶的正方体模型的渲染情况,左图是一个正常的正方体,外表面为正面,右图中的模型与左图相同,只是有一面的法线翻转到内侧,可以看到两个模型虽然形状一样,但渲染效果是不同的。 218 | 219 | (P.S. 那能不能让我们看到同时看到正反面呢,答案是可以的,3dsmax和three.js里都可以设置一个”双面“参数,就是适用于这种模型不封闭有缺口,但正反面都想看的这种情况的) 220 | 221 | ![img](https://pic1.zhimg.com/80/v2-884a6d5cb7839137e1508ad1eb2328a4_720w.jpg) 222 | 223 | 224 | 225 | #### OBJ 文件格式 226 | 227 | OBJ文件是一种被广泛使用的3D模型文件格式(obj为后缀名)。由Alias|Wavefront公司为3D建模和动画软件"Advanced Visualizer"开发的一种标准,适合用于3D软件模型之间的互导,也可以通过3dsmax、Maya等建模软件读写。 228 | 229 | ![img](https://pic3.zhimg.com/80/v2-da992e536b4af78023bfc6aedc03bcae_720w.jpg) 230 | 231 | ![img](https://pic1.zhimg.com/80/v2-f30e306cf70c3ce2fb643a9d9f65f63c_720w.jpg) 232 | 233 | 图上这个立方体的.obj文件内容大概就长下面的这个样子,相关解释已包含在注释中。 234 | 235 | ```python 236 | # "#"号开头是注释行 237 | # v(vertex)数据段: 模型顶点列表 238 | # 顶点位置信息,是xyz三维坐标 239 | # v开头的每一行描述一个顶点,行数等于顶点数。8个顶点所以有8行 240 | v 1.00 -1.00 -1.00 241 | v 1.00 1.00 1.00 242 | ...... 243 | # vt(vertex texture)数据段:模型顶点的纹理坐标列表 244 | # 顶点的纹理坐标信息,是xy二维坐标 245 | # vt开头的每一行描述一个纹理坐标,行数大于等于顶点数,因为一个模型顶点在贴图的UV坐标系中很可能对应多个顶点/纹理坐标。且坐标值范围是在0~1之间,这个模型中有14行。 246 | # 关于纹理坐标看图,本文不多解释纹理坐标,可参考文献[2]或自行百度 247 | vt 0.74 0.75 248 | vt 0.29 0.55 249 | ...... 250 | # vn(vertex normal)数据段:顶点法线列表 251 | # 三维法向量,xyz 252 | # vn开头的每一行描述一个法向量,行数大于等于顶点数。 前面介绍了,法线是与面相关的概念,但是现在的面是靠顶点来描述,拿示意图中的点"1"为例,它与参与构成了三个面,所以"顶点1"对应有3条法线 253 | # 可能你已经发现了,在这个立方体模型中,共面顶点的法向量的方向是相同的,也就是说这里面的数据会重复,所以在建模软件导出obj文件时有个优化的选项,勾选后的导出的法线列表数据中就不会有重复项,这里的例子优化后有6条法线* 254 | vn -1.00 0.00 0.00 255 | vn 1.00 0.00 0.00 256 | vn 0.00 1.00 0.00 257 | ...... 258 | # f(face):模型的三角面列表 259 | # f开头的每一行描述一个面 ,关键的来了,三个点组成一个面,怎样拿到这三个点呢?通过从1开始的索引,去前面的v、vt、vn列表中去取。 260 | # 总结一下就是:每一行定义1个面,1个面包含3个点,1个点具有“顶点/纹理坐标/法线”3个索引值,索引的是前面3个列表的信息。 261 | f 1/1/1 2/2/1 3/3/1 # 顶点1、顶点2、顶点3 组成的面 262 | f 2/2/1 3/3/1 4/4/1 # 顶点2、顶点3、顶点4 组成的面 263 | f 1/1/1 5/10/1 8/14/6 # 顶点1、顶点5、顶点8 组成的面 264 | ...... 265 | ``` 266 | 267 | 以上便是obj 文件的核心部分,此外还有一些数据段,记录如下。 268 | 269 | - o 对象名 270 | - g 组名 271 | - s 平滑组 272 | - usemtl 材质名 273 | - mtllib 材质库.mtl 274 | 275 | 276 | 277 | #### 其他3D模型文件格式 278 | 279 | > 通常,三维软件的输出格式分为三类。 280 | > 第一类是商业内核级别的数据格式,如ACIS商业内核的sat数据格式(*.sat 扩展名为SAT的文件);ParaSolid内核的X_T数据格式(*.X_T)。 281 | > 第二类是公共级别的数据格式,如Step的*.stp;IGES的*.igs(爱鸡屎,强烈建议不要使用!) 282 | > 第三类就是专用级的数据格式,就是每个软件自己特有的数据文件格式,如SW的SLDPRT,PROE的PRT等。 283 | 284 | 具体可以查看[Data Formats: 3D, Audio, Image,对模型文件格式总结,很全。 285 | 286 | 287 | 288 |  289 | 290 | 291 | 292 | --- 293 | 294 | ## 参考文章 295 | 296 | 1. [OBJ网格模型文件(上) - 学习随笔](https://zhuanlan.zhihu.com/p/38052123) 297 | 2. 《从传统渲染到可微渲染**:** 基本原理、方法和应用》 298 | 3. [[报告摘录] 可微渲染与神经渲染 简述](https://zhuanlan.zhihu.com/p/363358697) 299 | 4. [什么是可微分渲染(differentiable rendering)?](https://www.zhihu.com/question/364770565) 300 | 301 | 302 | 303 | 304 | 305 |  306 | 307 |  -------------------------------------------------------------------------------- /Notes/Qualcomm_RealTime_Accurate_ConsistentVideoSemanticSegmentationNotes.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccc013/DeepLearning_Notes/040201fb1654b01defdaf71db15f5a9bc1ca66fb/Notes/Qualcomm_RealTime_Accurate_ConsistentVideoSemanticSegmentationNotes.docx -------------------------------------------------------------------------------- /Notes/Quantization_Notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccc013/DeepLearning_Notes/040201fb1654b01defdaf71db15f5a9bc1ca66fb/Notes/Quantization_Notes.pdf -------------------------------------------------------------------------------- /Notes/常用功能函数代码.md: -------------------------------------------------------------------------------- 1 | 记录常用的一些功能函数代码,包括日志、展示结果、深度学习一些函数的实现,比如卷积层、BN 等; 2 | 3 | 4 | 5 | # 1. 日志写法 6 | 7 | 一个日志的写法 8 | 9 | 10 | ```python 11 | import logging 12 | def getLog(logName, logFile): 13 | log = logging.getLogger(logName) 14 | log.setLevel(logging.INFO) 15 | logFormat = logging.Formatter('%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s') 16 | # logFile = './log/log_request.txt' 17 | logHandler = TimedRotatingFileHandler(logFile, when='D', interval=30, backupCount=5) 18 | logHandler.setFormatter(logFormat) 19 | log.addHandler(logHandler) 20 | return log 21 | ``` 22 | 23 | 另一种写法: 24 | 25 | 26 | ```python 27 | import logging 28 | import time 29 | from datetime import timedelta 30 | 31 | 32 | class LogFormatter(): 33 | 34 | def __init__(self): 35 | self.start_time = time.time() 36 | 37 | def format(self, record): 38 | elapsed_seconds = round(record.created - self.start_time) 39 | 40 | prefix = "%s - line:%d - %s - %s - %s" % ( 41 | record.module, # 当前代码文件名字 42 | record.lineno, # 打印所在行数 43 | record.levelname, # 打印信息的级别 44 | time.strftime('%x %X'), # 当前时间 45 | timedelta(seconds=elapsed_seconds) # 代码运行所用时间 46 | ) 47 | message = record.getMessage() 48 | message = message.replace('\n', '\n' + ' ' * (len(prefix) + 3)) 49 | return "%s - %s" % (prefix, message) 50 | 51 | 52 | def create_logger(filepath): 53 | """ 54 | Create a logger. 55 | """ 56 | # create log formatter 57 | log_formatter = LogFormatter() 58 | 59 | # create file handler and set level to debug 60 | if filepath is not None: 61 | file_handler = logging.FileHandler(filepath, "a") 62 | file_handler.setLevel(logging.DEBUG) 63 | file_handler.setFormatter(log_formatter) 64 | 65 | # create console handler and set level to info 66 | console_handler = logging.StreamHandler() 67 | console_handler.setLevel(logging.INFO) 68 | console_handler.setFormatter(log_formatter) 69 | 70 | # create logger and set level to debug 71 | logger = logging.getLogger() 72 | logger.handlers = [] 73 | logger.setLevel(logging.DEBUG) 74 | logger.propagate = False 75 | if filepath is not None: 76 | logger.addHandler(file_handler) 77 | logger.addHandler(console_handler) 78 | 79 | # reset logger elapsed time 80 | def reset_time(): 81 | log_formatter.start_time = time.time() 82 | 83 | logger.reset_time = reset_time 84 | 85 | return logger 86 | ``` 87 | 88 | 89 | # 2. 展示结果 90 | 将结果图片用网页形式展示,包括保存结果图片和写入html 文件 91 | 92 | 93 | ```python 94 | # save image and create filesets for append_index() 95 | def save_images(img_infos, output_dir): 96 | output_image_dir = os.path.join(output_dir, "images") 97 | if not os.path.exists(output_image_dir): 98 | os.makedirs(output_image_dir) 99 | 100 | filesets = [] 101 | for i, infos in enumerate(img_infos): 102 | fileset = dict() 103 | img_path, true_label, predict_label = infos 104 | 105 | img_name = os.path.basename(img_path) 106 | fileset['images'] = img_name 107 | _name, _ext = os.path.splitext(img_name) 108 | fileset['name'] = _name 109 | fileset['true_label'] = true_label 110 | fileset['predict_label'] = predict_label 111 | # move image 112 | target_img_path = os.path.join(output_image_dir, img_name) 113 | shutil.copyfile(img_path, target_img_path) 114 | 115 | filesets.append(fileset) 116 | return filesets 117 | 118 | 119 | # create a html to show results 120 | def append_index(filesets, output_dir, html_name=None, title='result'): 121 | if html_name is None: 122 | index_path = os.path.join(output_dir, "result.html") 123 | else: 124 | index_path = os.path.join(output_dir, html_name) 125 | if os.path.exists(index_path): 126 | index = open(index_path, "a") 127 | else: 128 | index = open(index_path, "w") 129 | index.write(""" 130 | 131 | 132 | %s 133 | 134 | """ % title) 135 | 136 | count = 0 137 | for fileset in filesets: 138 | # index.write("%s" % fileset["name"]) 139 | index.write("

%d--%s

" % (count, fileset["name"])) 140 | index.write("") 141 | index.write("") 142 | for kind in ['images', 'true_label', 'predict_label']: 143 | index.write("") 146 | index.write("") 147 | 148 | index.write("") 149 | for kind in ['images', 'true_label', 'predict_label']: 150 | index.write("") 156 | count += 1 157 | index.write("
") 144 | index.write("

%s
" % kind) 145 | index.write("

") 151 | if kind == 'images': 152 | index.write("


" % fileset[kind]) 153 | else: 154 | index.write("

%s
" % fileset[kind]) 155 | index.write("

") 158 | 159 | return index_path 160 | ``` 161 | 162 | 163 | 164 |   165 | 166 | # 3. 如何解析命令行中的 bool 类型参数 167 | 参考代码 [https://github.com/jlezama/disentangling-jacobian/blob/master/conditional_image_generation/src/utils.py](https://github.com/jlezama/disentangling-jacobian/blob/master/conditional_image_generation/src/utils.py) 168 | 169 | 170 | ```python 171 | # 定义命令 172 | import argparse 173 | 174 | FALSY_STRINGS = {'off', 'false', '0'} 175 | TRUTHY_STRINGS = {'on', 'true', '1'} 176 | 177 | def bool_flag(s): 178 | """ 179 | Parse boolean arguments from the command line. 180 | """ 181 | if s.lower() in FALSY_STRINGS: 182 | return False 183 | elif s.lower() in TRUTHY_STRINGS: 184 | return True 185 | else: 186 | raise argparse.ArgumentTypeError("invalid value for a boolean flag. use 0 or 1") 187 | 188 | 189 | def attr_flag(s): 190 | """ 191 | Parse attributes parameters. 192 | """ 193 | n_cat = 1 194 | 195 | if s == "*": 196 | return s 197 | attr = s.split(',') 198 | assert len(attr) == len(set(attr)) 199 | attributes = [] 200 | for x in attr: 201 | if '.' not in x: 202 | attributes.append((x, n_cat)) 203 | else: 204 | split = x.split('.') 205 | assert len(split) == 2 and len(split[0]) > 0 206 | assert split[1].isdigit() and int(split[1]) >= n_cat 207 | attributes.append((split[0], int(split[1]))) 208 | return sorted(attributes, key=lambda x: (x[1], x[0])) 209 | 210 | parser = argparse.ArgumentParser(description='Images autoencoder') 211 | parser.add_argument("--attr", type=attr_flag, default="Smiling,Male", 212 | help="Attributes to classify") 213 | parser.add_argument("--instance_norm", type=bool_flag, default=False, 214 | help="Use instance normalization instead of batch normalization") 215 | params = parser.parse_args() 216 | ``` 217 | 218 | 219 | ## 打印参数 220 | 221 | 222 | ```python 223 | # 定义命令 224 | import argparse 225 | 226 | parser = argparse.ArgumentParser(description='Images autoencoder') 227 | parser.add_argument("--attr", type=attr_flag, default="Smiling,Male", 228 | help="Attributes to classify") 229 | parser.add_argument("--instance_norm", type=bool_flag, default=False, 230 | help="Use instance normalization instead of batch normalization") 231 | params = parser.parse_args() 232 | 233 | print('\n'.join('%s: %s' % (k, str(v)) for k, v 234 | in sorted(dict(vars(params)).items()))) 235 | ``` 236 | 237 | 238 | 239 | --- 240 | 241 | # 4. CNN 一些网络层的实现 242 | ## 4.1 卷积 243 | 参考:[python写卷积](https://zhuanlan.zhihu.com/p/146706558) 244 | ```python 245 | import numpy as np 246 | def conv(image, kernel, mode='same'): 247 | if mode == 'fill': 248 | h = kernel.shape[0] // 2 249 | w = kernel.shape[1] // 2 250 | image = np.pad(image, ((h, h), (w, w), (0, 0)), 'constant') 251 | conv_b = _convolve(image[:, :, 0], kernel) 252 | conv_g = _convolve(image[:, :, 1], kernel) 253 | conv_r = _convolve(image[:, :, 2], kernel) 254 | res = np.dstack([conv_b, conv_g, conv_r]) 255 | return res 256 | def _convolve(image, kernel): 257 | h_kernel, w_kernel = kernel.shape 258 | h_image, w_image = image.shape 259 | res_h = h_image - h_kernel + 1 260 | res_w = w_image - w_kernel + 1 261 | res = np.zeros((res_h, res_w), np.uint8) 262 | for i in range(res_h): 263 | for j in range(res_w): 264 | res[i, j] = normal(image[i:i+h_kernel, j:j+w_kernel], kernel) 265 | return res 266 | def normal(image, kernel): 267 | res = np.multiply(image, kernel).sum() 268 | if res > 255: 269 | return 255 270 | else: 271 | return res 272 | ``` 273 | **其实也可以将卷积看成poolsize=(kernel.size(0), kernel.size(1))的pooling,只不过变成了与核函数的求和操作:** 274 | ** 275 | ```python 276 | import numpy as np 277 | def normal(image, kernel): 278 | row, col = len(kernel), len(kernel[0]) 279 | res = 0 280 | for i in range(row): 281 | for j in range(col): 282 | res += image[i][j]*kernel[i][j] 283 | if res > 255: 284 | return 255 285 | else: 286 | return res 287 | def conv(image, kernel, stride): 288 | h_img, w_img = len(image), len(image[0]) 289 | h_ker, w_ker = len(kernel), len(kernel[0]) # size 290 | # output 291 | out_row, out_col = h_img // stride, w_img // stride 292 | row_remainder, col_remainder = h_img % stride, w_img % stride 293 | if row_remainder != 0: 294 | out_row += 1 295 | if col_remainder != 0: 296 | out_col += 1 297 | output = np.zeros((out_row, out_col)) 298 | # padding 299 | tmp = np.lib.pad(image, ((0, h_ker-row_remainder), (0, w_ker-col_remainder)), 'edge') 300 | # conv 301 | for i in range(out_row): 302 | for j in range(out_col): 303 | x = i*stride 304 | y = j*stride 305 | output[i, j] = normal(tmp[x:x+h_ker, y:y+w_ker], kernel) 306 | return output 307 | ``` 308 | 309 | 310 | 311 | 312 | ## 4.2 pooling 313 | 参考:[pooling的原理与Python实现](https://www.cnblogs.com/haoguoeveryone/p/haoguo_3.html) 314 | 315 | ```python 316 | def pooling(inputMap,poolSize=3,poolStride=2,mode='max'): 317 | """INPUTS: 318 | inputMap - input array of the pooling layer 319 | poolSize - X-size(equivalent to Y-size) of receptive field 320 | poolStride - the stride size between successive pooling squares 321 | 322 | OUTPUTS: 323 | outputMap - output array of the pooling layer 324 | 325 | Padding mode - 'edge' 326 | """ 327 | # inputMap sizes 328 | in_row,in_col = np.shape(inputMap) 329 | 330 | # outputMap sizes 331 | out_row,out_col = int(np.floor(in_row/poolStride)),int(np.floor(in_col/poolStride)) 332 | row_remainder,col_remainder = np.mod(in_row,poolStride),np.mod(in_col,poolStride) 333 | if row_remainder != 0: 334 | out_row +=1 335 | if col_remainder != 0: 336 | out_col +=1 337 | outputMap = np.zeros((out_row,out_col)) 338 | 339 | # padding 340 | temp_map = np.lib.pad(inputMap, ((0,poolSize-row_remainder),(0,poolSize-col_remainder)), 'edge') 341 | 342 | # max pooling 343 | for r_idx in range(0,out_row): 344 | for c_idx in range(0,out_col): 345 | startX = c_idx * poolStride 346 | startY = r_idx * poolStride 347 | poolField = temp_map[startY:startY + poolSize, startX:startX + poolSize] 348 | poolOut = np.max(poolField) 349 | #poolOut = np.mean(poolField) # 均值池化 350 | outputMap[r_idx,c_idx] = poolOut 351 | 352 | 353 | # retrun outputMap 354 | return outputMap 355 | # 测试实例 356 | test = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]]) 357 | test_result = pooling(test, 2, 2, 'max') 358 | print(test_result) 359 | ``` 360 | 361 | 362 | 363 | ## 4.3 归一化方法 364 | 365 | ### Batch Normalization 366 | 367 | 实现方法 1: 368 | 369 | ```python 370 | import numpy as np 371 | 372 | def Batchnorm(x, gamma, beta, bn_param): 373 | 374 | # x_shape:[B, C, H, W] 375 | running_mean = bn_param['running_mean'] 376 | running_var = bn_param['running_var'] 377 | results = 0. 378 | eps = 1e-5 379 | 380 | x_mean = np.mean(x, axis=(0, 2, 3), keepdims=True) 381 | x_var = np.var(x, axis=(0, 2, 3), keepdims=True0) 382 | x_normalized = (x - x_mean) / np.sqrt(x_var + eps) 383 | results = gamma * x_normalized + beta 384 | 385 | # 因为在测试时是单个图片测试,这里保留训练时的均值和方差,用在后面测试时用 386 | running_mean = momentum * running_mean + (1 - momentum) * x_mean 387 | running_var = momentum * running_var + (1 - momentum) * x_var 388 | 389 | bn_param['running_mean'] = running_mean 390 | bn_param['running_var'] = running_var 391 | 392 | return results, bn_param 393 | ``` 394 | 395 | 基于 PyTorch 实现 BN 层 396 | 397 | ```python 398 | def batch_norm(is_training, X, gamma, beta, moving_mean, moving_var, eps, momentum): 399 | # 判断当前模式是训练模式还是推理模式 400 | if not is_training: 401 | # 如果是在推理模式下,直接使用传入的移动平均所得的均值和方差 402 | X_hat = (X - moving_mean) / torch.sqrt(moving_var + eps) 403 | else: 404 | assert len(X.shape) in (2, 4) 405 | if len(X.shape) == 2: 406 | # 使用全连接层的情况,计算特征维上的均值和方差 407 | mean = X.mean(dim=0) 408 | var = ((X - mean) ** 2).mean(dim=0) 409 | else: 410 | # 使用二维卷积层的情况,计算通道维上(axis=1)的均值和方差。这里我们需要保持X的形状以便后面可以做广播运算 411 | # torch.Tensor 高维矩阵的表示: (nSample)x C x H x W,所以对C维度外的维度求均值 412 | mean = X.mean(dim=0, keepdim=True).mean(dim=2, keepdim=True).mean(dim=3, keepdim=True) 413 | var = ((X - mean) ** 2).mean(dim=0, keepdim=True).mean(dim=2, keepdim=True).mean(dim=3, keepdim=True) 414 | # 训练模式下用当前的均值和方差做标准化 415 | X_hat = (X - mean) / torch.sqrt(var + eps) 416 | # 更新移动平均的均值和方差 417 | moving_mean = momentum * moving_mean + (1.0 - momentum) * mean 418 | moving_var = momentum * moving_var + (1.0 - momentum) * var 419 | Y = gamma * X_hat + beta # 拉伸和偏移(变换重构) 420 | return Y, moving_mean, moving_var 421 | 422 | class BatchNorm(nn.Module): 423 | def __init__(self, num_features, num_dims): # num_features就是通道数 424 | super(BatchNorm, self).__init__() 425 | if num_dims == 2: 426 | shape = (1, num_features) 427 | else: 428 | shape = (1, num_features, 1, 1) 429 | # 参与求梯度和迭代的拉伸和偏移参数,分别初始化成0和1 430 | self.gamma = nn.Parameter(torch.ones(shape)) 431 | self.beta = nn.Parameter(torch.zeros(shape)) 432 | # 不参与求梯度和迭代的变量,全在内存上初始化成0 433 | self.moving_mean = torch.zeros(shape) 434 | self.moving_var = torch.zeros(shape) 435 | 436 | def forward(self, X): 437 | # 如果X不在内存上,将moving_mean和moving_var复制到X所在显存上 438 | if self.moving_mean.device != X.device: 439 | self.moving_mean = self.moving_mean.to(X.device) 440 | self.moving_var = self.moving_var.to(X.device) 441 | # 保存更新过的moving_mean和moving_var, Module实例的traning属性默认为true, 调用.eval()后设成false 442 | Y, self.moving_mean, self.moving_var = batch_norm(self.training, 443 | X, self.gamma, self.beta, self.moving_mean, 444 | self.moving_var, eps=1e-5, momentum=0.9) 445 | return Y 446 | ``` 447 | 448 | 449 | 450 | ### Layer Normalization 451 | 452 | 实现如下: 453 | 454 | ```python 455 | def ln(x, b, s): 456 | _eps = 1e-5 457 | output = (x - x.mean(1)[:,None]) / tensor.sqrt((x.var(1)[:,None] + _eps)) 458 | output = s[None, :] * output + b[None,:] 459 | return output 460 | ``` 461 | 462 | 用在四维图像上: 463 | 464 | ```python 465 | def Layernorm(x, gamma, beta): 466 | 467 | # x_shape:[B, C, H, W] 468 | results = 0. 469 | eps = 1e-5 470 | 471 | x_mean = np.mean(x, axis=(1, 2, 3), keepdims=True) 472 | x_var = np.var(x, axis=(1, 2, 3), keepdims=True0) 473 | x_normalized = (x - x_mean) / np.sqrt(x_var + eps) 474 | results = gamma * x_normalized + beta 475 | return results 476 | ``` 477 | 478 | 479 | 480 | ### Group Normalization 481 | 482 | 代码实现: 483 | 484 | ```python 485 | def GroupNorm(x, gamma, beta, G=16): 486 | 487 | # x_shape:[B, C, H, W] 488 | results = 0. 489 | eps = 1e-5 490 | x = np.reshape(x, (x.shape[0], G, x.shape[1]/16, x.shape[2], x.shape[3])) 491 | 492 | x_mean = np.mean(x, axis=(2, 3, 4), keepdims=True) 493 | x_var = np.var(x, axis=(2, 3, 4), keepdims=True0) 494 | x_normalized = (x - x_mean) / np.sqrt(x_var + eps) 495 | results = gamma * x_normalized + beta 496 | return results 497 | ``` 498 | 499 | 500 | 501 | ### Instance Normalization 502 | 503 | 代码实现: 504 | 505 | ```python 506 | def Instancenorm(x, gamma, beta): 507 | 508 | # x_shape:[B, C, H, W] 509 | results = 0. 510 | eps = 1e-5 511 | 512 | x_mean = np.mean(x, axis=(2, 3), keepdims=True) 513 | x_var = np.var(x, axis=(2, 3), keepdims=True0) 514 | x_normalized = (x - x_mean) / np.sqrt(x_var + eps) 515 | results = gamma * x_normalized + beta 516 | return results 517 | ``` 518 | 519 | 520 | 521 | ### Switchable Normalization 522 | 523 | 代码实现: 524 | 525 | ```python 526 | def SwitchableNorm(x, gamma, beta, w_mean, w_var): 527 | # x_shape:[B, C, H, W] 528 | results = 0. 529 | eps = 1e-5 530 | 531 | mean_in = np.mean(x, axis=(2, 3), keepdims=True) 532 | var_in = np.var(x, axis=(2, 3), keepdims=True) 533 | 534 | mean_ln = np.mean(x, axis=(1, 2, 3), keepdims=True) 535 | var_ln = np.var(x, axis=(1, 2, 3), keepdims=True) 536 | 537 | mean_bn = np.mean(x, axis=(0, 2, 3), keepdims=True) 538 | var_bn = np.var(x, axis=(0, 2, 3), keepdims=True) 539 | 540 | mean = w_mean[0] * mean_in + w_mean[1] * mean_ln + w_mean[2] * mean_bn 541 | var = w_var[0] * var_in + w_var[1] * var_ln + w_var[2] * var_bn 542 | 543 | x_normalized = (x - mean) / np.sqrt(var + eps) 544 | results = gamma * x_normalized + beta 545 | return results 546 | ``` 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | ------ 555 | 556 | # 5. 目标检测 557 | 558 | ## NMS 559 | 560 | NMS用来去掉重复的框。输入前面得到的框,对于每一类,按照score进行降序排序,最大的那个一定保留,然后和其他的框计算IOU。IOU大于一定阈值视为重复的框,丢弃掉。 561 | 562 | ```python 563 | import numpy as np 564 | def nms(dets, thresh): 565 | x1 = dets[:, 0] # bbox top_x 566 | y1 = dets[:, 1] # bbox top_y 567 | x2 = dets[:, 2] # bbox bottom_x 568 | y2 = dets[:, 3] # bbox bottom_y 569 | scores = dets[:, 4] # 分类score 570 | areas = (x2 - x1 + 1) * (y2 - y1 + 1) 571 | order = scores.argsort()[::-1] # 按score做降序排序 572 | keep = [] 573 | while order.size > 0: 574 | i = order[0] 575 | keep.append(i) 576 | xx1 = np.maximum(x1[i], x1[order[1:]]) 577 | yy1 = np.maximum(y1[i], y1[order[1:]]) 578 | xx2 = np.minimum(x2[i], x2[order[1:]]) 579 | yy2 = np.minimum(y2[i], y2[order[1:]]) 580 | w = np.maximum(0.0, xx2 - xx1 + 1) 581 | h = np.maximum(0.0, yy2 - yy1 + 1) 582 | intersection = w * h 583 | iou = intersection / (areas[i] + areas[order[1:]] - intersection) 584 | inds = np.where(iou <= thresh)[0] 585 | order = order[inds + 1] # 加1的原因。假设一个类别有n个框,这里的索引是n-1个iou是对应的, 586 | # 这里要映射到原来长度为n的order,所以加1。 587 | return keep 588 | ``` 589 | 590 | 591 | 592 | 593 | 594 | ------ 595 | 596 | # 6. 机器学习 597 | 598 | ## Kmeans 聚类 599 | 600 | 下面是简易版本的实现。 601 | 602 | 注意,`np.random.randint()` 是取值范围是左闭右开区间,python 自带的 `random.randint()` 是闭区间。 603 | 604 | ```python 605 | import numpy as np 606 | import random 607 | 608 | def kmeans(data, k): 609 | m = len(data) # 样本个数 610 | n = len(data[0]) # 维度 611 | cluster_center = np.zeros((k, n)) # k个聚类中心 612 | 613 | # 选择合适的初始聚类中心 614 | # 在已有数据中随机选择聚类中心 615 | # 也可以直接用随机的聚类中心 616 | 617 | init_list = np.random.randint(low=0, high=m, size=k) # [0, m) 618 | for index, j in enumerate(init_list): 619 | cluster_center[index] = data[j][:] 620 | 621 | # 聚类 622 | cluster = np.zeros(m, dtype=np.int) - 1 # 所有样本尚未聚类 623 | cc = np.zeros((k, n)) # 下一轮的聚类中心 624 | c_number = np.zeros(k) # 每个簇样本的数目 625 | 626 | for times in range(1000): 627 | for i in range(m): 628 | c = nearst(data[i], cluster_center) 629 | cluster[i] = c # 第i个样本归于第c簇 630 | c_number[c] += 1 631 | cc[c] += data[i] 632 | for i in range(k): 633 | cluster_center[i] = cc[i] / c_number[i] 634 | cc.flat = 0 635 | c_number.flat = 0 636 | return cluster 637 | 638 | def nearst(data, cluster_center): 639 | nearst_center_index = 0 640 | dis = np.sum((cluster_center[0] - data) ** 2) 641 | for index, center in enumerate(cluster_center): 642 | dis_temp = np.sum((center - data) ** 2) 643 | if dis_temp < dis: 644 | nearst_center_index = index 645 | dis = dis_temp 646 | return nearst_center_index 647 | 648 | if __name__ == "__main__": 649 | data = [[0,0], [1,0], [0,1], [100,100], [101,101], [102, 100], [-100,-100], [-101,-101], [-102, -100]] 650 | data = np.array(data) 651 | cluster = kmeans(data, 3) 652 | print(cluster) 653 | # [0 0 0 1 1 1 2 2 2] 每个样本对应的类别,结果有随机性 654 | ``` 655 | 656 | 657 | 658 | ## Softmax 659 | 660 | 661 | 662 | ```python 663 | import numpy as np 664 | 665 | def convert_label_to_onehot(classes, labels): 666 | """ 667 | classes: 类别数 668 | labels: array,shape=(N,) 669 | """ 670 | return np.eye(classes)[labels].T 671 | 672 | def softmax(logits): 673 | """logits: array, shape=(N, C)""" 674 | res = np.zeros_like(logits) 675 | for i, row in enumerate(logits): 676 | exp_sum = np.sum(np.exp(row)) 677 | for j, val in enumerate(row): 678 | res[i,j] = np.exp(val)/ exp_sum 679 | return res 680 | 681 | if __name__ == '__main__': 682 | # 有四个样本,有三个类别 683 | logits = np.array([[0, 0.45, 0.55], 684 | [0.9, 0.05, 0.05], 685 | [0.4, 0.6, 0], 686 | [1, 0, 0]]) 687 | pred = np.argmax(softmax(logits), axis=1) 688 | print(pred) 689 | ``` 690 | 691 | 692 | 693 | ## PR曲线和 ROC 曲线的绘制 694 | 695 | 这两种曲线是评价分类模型performance的常用方法。其中,PR图的纵坐标是precision,横坐标是recall;ROC曲线的纵坐标是True Positive Rate,横坐标是False Positive Rate,它们的公式如下: 696 | $$ 697 | precision = \frac{TP}{TP+FP}\\ 698 | recall = \frac{TP}{TP+FN}\\ 699 | TPR = \frac{TP}{TP+FN}\\ 700 | FPR=\frac{FP}{TN+FP} 701 | $$ 702 | 703 | 704 | ```python 705 | import numpy as np 706 | import matplotlib.pyplot as plt 707 | data_number = 50 708 | labels = np.random.randint(0, 2, size=data_number) # 真实的类别 709 | scores = np.random.choice(np.arange(0.1, 1, 0.01), data_number) # 模型判断样本为类别1的置信度 710 | 711 | 712 | def pr_curve(y, pred): 713 | pos = np.sum(y == 1) 714 | neg = np.sum(y == 0) 715 | pred_sort = np.sort(pred)[::-1] 716 | index = np.argsort(pred)[::-1] 717 | y_sort = y[index] 718 | print(y_sort) 719 | 720 | pre = [] 721 | rec = [] 722 | for i, item in enumerate(pred_sort): 723 | if i == 0: 724 | pre.append(1) 725 | rec.append(0) 726 | else: 727 | pre.append(np.sum((y_sort[:i] == 1)) / i) 728 | rec.append(np.sum((y_sort[:i] == 1)) / pos) 729 | 730 | # 画图 731 | plt.plot(rec, pre, 'k') 732 | # plt.legend(loc='lower right') 733 | plt.title('PR curve') 734 | plt.plot([(0, 0), (1, 1)], 'r--') 735 | plt.xlim([-0.01, 1.01]) 736 | plt.ylim([-0.01, 01.01]) 737 | plt.ylabel('precision') 738 | plt.xlabel('recall') 739 | plt.show() 740 | 741 | 742 | def roc_curve(y, pred): 743 | pos = np.sum(y == 1) 744 | neg = np.sum(y == 0) 745 | pred_sort = np.sort(pred)[::-1] 746 | index = np.argsort(pred)[::-1] 747 | y_sort = y[index] 748 | print(y_sort) 749 | tpr = [] 750 | fpr = [] 751 | thr = [] 752 | for i, item in enumerate(pred_sort): 753 | tpr.append(np.sum((y_sort[:i] == 1)) / pos) 754 | fpr.append(np.sum((y_sort[:i] == 0)) / neg) 755 | thr.append(item) 756 | 757 | # 画图 758 | plt.plot(fpr, tpr, 'k') 759 | plt.title('ROC curve') 760 | plt.plot([(0, 0), (1, 1)], 'r--') 761 | plt.xlim([-0.01, 1.01]) 762 | plt.ylim([-0.01, 01.01]) 763 | plt.ylabel('True Positive Rate') 764 | plt.xlabel('False Positive Rate') 765 | plt.show() 766 | 767 | 768 | pr_curve(labels, scores) 769 | roc_curve(labels, scores) 770 | ``` 771 | 772 | 773 | 774 | ## 数据分析 775 | 776 | ### 探索性数据分析 777 | 778 | 参考: 779 | 780 | - [使用pandas_profiling用2行代码完成探索性数据分析](https://mp.weixin.qq.com/s/qRje-oIh_XM67QMrToYslA) 781 | 782 | 783 | 784 | **pandas_profiling** 是可以将pandas数据框快速转化为描述性数据分析报告的package,一行代码即可生成内容丰富的EDA内容,两行代码即可将报告以`.html`格式保存 785 | 786 | 787 | 788 | 常规的对数据做 EDA 的做法如下所示: 789 | 790 | ```python 791 | import numpy as np 792 | import pandas as pd 793 | adult = pd.read_csv('../adult.data') 794 | # 查看前 5 行数据的信息 795 | adult.head() 796 | 797 | # 对数据进行统计描述,展示数据的均值、方差、最大值最小值等信息 798 | adult.describe() 799 | 800 | # 查看变量信息和缺失情况 801 | adult.info() 802 | ``` 803 | 804 | 这是最简单最快速了解一个数据集的方法。当然,更深层次的EDA一定是要借助统计图形来展示的。基于scipy、matplotlib和seaborn等工具的展示这里权且略过。 805 | 806 | 807 | 808 | 现在我们有了pandas_profiling。上述过程以及各种统计相关性计算、统计绘图全部由pandas_profiling打包搞定了。pandas_profiling安装,包括pip、conda和源码三种安装方式。 809 | 810 | pip: 811 | 812 | ``` 813 | pip install pandas-profilingpip install https://github.com/pandas-profiling/pandas-profiling/archive/master.zip 814 | ``` 815 | 816 | conda: 817 | 818 | ``` 819 | conda install -c conda-forge pandas-profiling 820 | ``` 821 | 822 | source: 823 | 824 | 先下载源码文件,然后解压到setup.py所在的文件目录下: 825 | 826 | ``` 827 | python setup.py install 828 | ``` 829 | 830 | 再来看pandas_profiling基本用法,用pandas将数据读入之后,对数据框直接调用profile_report方法生成EDA分析报告,然后使用to_file方法另存为.html文件。 831 | 832 | ``` 833 | profile = df.profile_report(title="Census Dataset")profile.to_file(output_file=Path("./census_report.html")) 834 | ``` 835 | 836 | 看看报告效果如何。pandas-profiling EDA报告包括数据整体概览、变量探索、相关性计算、缺失值情况和抽样展示等5个方面。 837 | 838 | 839 | 840 | 841 | 842 | ------ 843 | 844 | # 参考 845 | 846 | 1. [【基础算法】常见的ML、DL编程题](https://mp.weixin.qq.com/s/jHS2Vl3Msn7t94_YsrfrKA) 847 | 848 | -------------------------------------------------------------------------------- /Notes/数学基础.md: -------------------------------------------------------------------------------- 1 | 简单学习并记录数学方面的知识点,对于深度学习来说,主要需要的数学包括这几个方面: 2 | 3 | 1. 线性代数 4 | 2. 概率论 5 | 3. 微积分 6 | 7 | 8 | 9 | # 1.线性代数 10 | 11 | ## 1.1 向量和矩阵 12 | 13 | ### 1.1.1 标量、向量、矩阵、张量之间的联系 14 | 15 | **标量(scalar)** 16 | 17 | 一个标量表示一个单独的数,它不同于线性代数中研究的其他大部分对象(通常是多个数的数组)。我们用斜体表示标量。标量通常被赋予小写的变量名称。 一般会明确标量属于哪种类型,比如定义实数标量时,会说“令 $s\in R$ 表示一条线的斜率”。 18 | 19 | **向量(vector)** 20 | 21 | 一个向量表示一组有序排列的数。通过次序中的索引,我们可以确定每个单独的数。通常我们赋予向量粗体的小写变量名称,比如xx。向量中的元素可以通过带脚标的斜体表示。向量$X$的第一个元素是$X_1$,第二个元素是$X_2$,以此类推。我们也会注明存储在向量中的元素的类型(实数、虚数等)。 22 | 23 | 一个向量如下所示,一个向量可以看作空间中的点,即每个元素可以表示不同坐标轴上的坐标。 24 | $$ 25 | x = 26 | \left[ 27 | \begin{matrix} 28 | x_1 \\ 29 | x_2 \\ 30 | x_3 \\ 31 | \cdots \\ 32 | x_n 33 | \end{matrix} 34 | \right] 35 | $$ 36 | 37 | 38 | 39 | 40 | **矩阵(matrix)** 41 | 42 | 矩阵是具有相同特征和纬度的对象的集合,表现为一张二维数据表。其意义是一个对象表示为矩阵中的一行,一个特征表示为矩阵中的一列,每个特征都有数值型的取值。通常会赋予矩阵粗体的大写变量名称,比如$A$。 43 | 44 | 一个矩阵的表示例子如下所示: 45 | $$ 46 | A = 47 | \left[ 48 | \begin{matrix} 49 | A_{1,1} & A_{1,2} \\ 50 | A_{2,1} & A_{2,2} \\ 51 | \end{matrix} 52 | \right] 53 | $$ 54 | 55 | **转置**是矩阵的重要操作之一,其转置是以对角线为轴的镜像,这条从左上角到右下角的对角线被称为**主对角线**,定义如下: 56 | $$ 57 | (A^T){i,j} = A_{j,i} 58 | $$ 59 | 一个示例操作如下: 60 | $$ 61 | A = 62 | \left[ 63 | \begin{matrix} 64 | A_{1,1} & A_{1,2} \\ 65 | A_{2,1} & A_{2,2} \\ 66 | A_{3,1} & A_{3,2} 67 | \end{matrix} 68 | \right] 69 | ==> 70 | A^T = 71 | \left[ 72 | \begin{matrix} 73 | A_{1,1} & A_{2,1} & A_{3, 1} \\ 74 | A_{1,2} & A_{2,2} & A_{3,2}\\ 75 | \end{matrix} 76 | \right] 77 | $$ 78 | 79 | 从一个 $3\times 2$ 的矩阵变为了 $ 2\times 3$ 的矩阵。 80 | 81 | 82 | 83 | **张量(tensor)** 84 | 85 | 在某些情况下,我们会讨论坐标超过两维的数组。一般地,一个数组中的元素分布在若干维坐标的规则网格中,我们将其称之为张量。使用 $A$ 来表示张量“A”。张量$A$中坐标为$(i,j,k)$的元素记作$A_{(i,j,k)}$。 86 | 87 | 88 | 89 | **四者之间关系** 90 | 91 | (来自深度学习 500 问第一章数学基础) 92 | 93 | > 标量是0阶张量,向量是一阶张量。举例: 94 | > ​标量就是知道棍子的长度,但是你不会知道棍子指向哪儿。 95 | > ​向量就是不但知道棍子的长度,还知道棍子指向前面还是后面。 96 | > ​张量就是不但知道棍子的长度,也知道棍子指向前面还是后面,还能知道这棍子又向上/下和左/右偏转了多少。 97 | 98 | 99 | 100 | 101 | 102 | ### 1.1.2 张量与矩阵的区别 103 | 104 | - 从代数角度讲, 矩阵它是向量的推广。向量可以看成一维的“表格”(即分量按照顺序排成一排), 矩阵是二维的“表格”(分量按照纵横位置排列), 那么$n$阶张量就是所谓的$n$维的“表格”。 张量的严格定义是利用线性映射来描述。 105 | - 从几何角度讲, 矩阵是一个真正的几何量,也就是说,它是一个不随参照系的坐标变换而变化的东西。向量也具有这种特性。 106 | - 张量可以用3×3矩阵形式来表达。 107 | - 表示标量的数和表示向量的三维数组也可分别看作1×1,1×3的矩阵。 108 | 109 | 110 | 111 | ### 1.1.3 矩阵和向量相乘结果 112 | 113 | 若使用爱因斯坦求和约定(Einstein summation convention),矩阵$A$, $B$相乘得到矩阵 $C$ 可以用下式表示: 114 | $$ AB = C ==> a_{ik}*b_{kj}=c_{ij} $$ 115 | 116 | 其中,$a_{ik}$, $b_{kj}$, $c_{ij}$分别表示矩阵$A, B, C$的元素,$k$出现两次,是一个哑变量(Dummy Variables)表示对该参数进行遍历求和。 117 | 118 | 用一个例子表示就是: 119 | $$ 120 | A= 121 | \left[ 122 | \begin{matrix} 123 | A_{1,1} & A_{1,2} \\ 124 | A_{2,1} & A_{2,2} \\ 125 | \end{matrix} 126 | \right] \ 127 | B = 128 | \left[ 129 | \begin{matrix} 130 | B_{1,1} & B_{1,2} \\ 131 | B_{2,1} & B_{2,2} \\ 132 | \end{matrix} 133 | \right] \\ 134 | A \times B = C = 135 | \left[ 136 | \begin{matrix} 137 | A_{1,1}\times B_{1,1}+A_{1,2}\times B_{2,1} & A_{1,1}\times B_{1,2}+A_{1,2}\times B_{2,2} \\ 138 | A_{2,1}\times B_{1,1}+A_{2,2}\times B_{2,1} & A_{2,1}\times B_{1,2}+A_{2,2}\times B_{2,2} \\ 139 | \end{matrix} 140 | \right] 141 | = 142 | \left[ 143 | \begin{matrix} 144 | C_{1,1} & C_{1,2} \\ 145 | C_{2,1} & C_{2,2} \\ 146 | \end{matrix} 147 | \right] 148 | $$ 149 | 所以矩阵相乘有一个前提,**矩阵 A 的列数必须和矩阵 B 的行数相等**,也就是如果 A 的维度是 $m\times n$,B 的维度必须是 $n \times p$,相乘得到的 C 矩阵的维度就是 $m\times p$。 150 | 151 | 另外还有一种矩阵乘法,是矩阵对应元素相乘,这种称为**元素对应乘积,或者 Hadamard 乘积**,记为 A ⊙ B 152 | 153 | 154 | 而矩阵和向量相乘可以看成是矩阵相乘的一个特殊情况,例如:矩阵$B$是一个$n \times 1$的矩阵。 155 | 156 | 157 | 158 | 矩阵乘积满足这些定律: 159 | 160 | 1. 服从分配率:A(B+C) = AB + AC 161 | 2. 服从结合律:A(BC) = (AB)C 162 | 163 | 但是**不服从交换律**,即 AB 不一定等于 BA。 164 | 165 | 矩阵的乘积满足:$(AB)^T = A^TB^T$ 166 | 167 | 168 | 169 | 两个相同维度的向量 x 和 y 的点积(dot product),可以看作矩阵乘积--$x^Ty$。也就是说可以将矩阵乘积 $C=AB$ 中计算 $C_{i,j}$的步骤看作是 A 的第 i 行和 B 的第 j 列之间的点积。毕竟,矩阵的每一行或者每一列都是一个向量。 170 | 171 | 而向量的点积是满足交换律的: 172 | $$ 173 | x^Ty = y^Tx 174 | $$ 175 | 证明主要是根据: 176 | 177 | 1. 两个向量的点积是标量 178 | 2. 标量的转置也是自身 179 | 180 | 所以有: 181 | $$ 182 | x^Ty = (x^Ty)^T = xy^T 183 | $$ 184 | 185 | ### 1.1.4 单位矩阵和逆矩阵 186 | 187 | 单位矩阵的定义如下,**用 I 表示单位矩阵,任何向量和单位矩阵相乘,都不会改变**,即: 188 | $$ 189 | \forall x \in R^n, I_n x = x \tag{1-1-8} 190 | $$ 191 | 单位矩阵的结构很简单,就是主对角线是 1,其他位置是 0,如下图所示的单位矩阵 $I_3$ : 192 | $$ 193 | \left[ 194 | \begin{matrix} 195 | 1 & 0 & 0 \\ 196 | 0 & 1 & 0 \\ 197 | 0 & 0 & 1 198 | \end{matrix} 199 | \right] 200 | $$ 201 | 而逆矩阵记作 $A^{-1}$,其满足如下条件: 202 | $$ 203 | A^{-1}A=I_n 204 | $$ 205 | 206 | 207 | 208 | ### 1.1.5 线性方程组和线性相关 209 | 210 | 现在有一个线性方程组,如下所示: 211 | $$ 212 | Ax = b 213 | $$ 214 | 其中,$A\in R^{m\times n}$ 是已知的矩阵,$b\in R^m$ 是已知的向量,然后 $x\in R^n$ 是需要求解的未知向量。 215 | 216 | 这里根据矩阵相乘(x 相当于一个 $n\times 1$ 的矩阵),可以将上述公式拓展开来: 217 | $$ 218 | A_{1,:}x = b_1 ==> A_{1,1}x_1 + A_{1,2}x_2+\cdots+A_{1,n}x_n = b_1 \\ 219 | A_{2,:}x = b_2 ==> A_{2,1}x_1 + A_{2,2}x_2+\cdots+A_{2,n}x_n = b_2 \\ 220 | \cdots \\ 221 | A_{m,:}x = b_m ==> A_{m,1}x_1 + A_{m,2}x_2+\cdots+A_{m,n}x_n = b_m \\ 222 | $$ 223 | 在我们定义了逆矩阵后,那么可以这么求解: 224 | $$ 225 | Ax=b\\ 226 | A^{-1}Ax = A^{-1}b\\ 227 | I_nx = A^{-1}b \\ 228 | x = A^{-1}b 229 | $$ 230 | 所以求解的关键就是是否存在一个逆矩阵,并找到它。 231 | 232 | 当逆矩阵$A^{-1}$存在的时候,对每个向量 b 肯定恰好存在一个解。 233 | 234 | 但对于方程组来说,向量 b 的某些值,有可能不存在解,或者有无限多个解,不存在多于1 个解,但有限解的情况,比如 x 和 y 都是方程组的解,则有: 235 | $$ 236 | z = \alpha x + (1-\alpha)y 237 | $$ 238 | 其中,$\alpha$ 是任意实数,那么 z 也是方程组的解,这种组合是无限的,所以不存在有限解(多于 1 个)。 239 | 240 | 确定 Ax=b 是否有解,**关键是确定向量 b 是否在 A 列向量的生成子空间中**,这个特殊的生成子空间,被称为 A 的列空间或者 A 的值域。 241 | 242 | > 一组向量的线性组合是指每个向量乘以对应标量系数之后的和,即 $\sum_i c_i v^{(i)}$ 243 | > 244 | > 一组向量的生成子空间是原始向量线性组合后所能抵达的点的集合。 245 | 246 | 那么为了让上述成立,**应该让 A 的列空间构成整个 $R^m$ 空间**,如果这个空间某个点不在 A 的列空间,那么对应的 b 会使得方程无解。而要让其成立,**即要满足不等式 $n\ge m$ **。 247 | 248 | 但该不等式只是方程对每个 b 有解的必要条件,非充分条件。因为存在一种情况,某些列向量可能是冗余的,比如一个 $2\times 2$的矩阵,如果两个列向量都是相同的,那该矩阵的列空间和它的一个列向量作为矩阵的列空间是一样的,并不能满足覆盖了整个 $R^2$ 空间。 249 | 250 | 这种冗余也被称为**线性相关**,而**如果一组向量中任意一个向量都不能表示为其他向量的线性组合,则这组向量称为线性无关**。 251 | 252 | 所以,**如果一个矩阵的列空间要覆盖整个 $R^m$,那么该矩阵必须包含至少一组m 个线性无关的向量,这才是对每个 b 都有解的充分必要条件**。 253 | 254 | 此外,要**让矩阵可逆**,还必须保证 Ax=b 对每个 b 的取值至多只有一个解,那必须保证该矩阵至多有 m 个列向量,否则方程有不止一个解。 255 | 256 | 综上,**那么矩阵就必须是方阵,也就是 m = n,并且所有列向量都是线性无关的**。一个列向量都是线性无关的方阵被称为是**奇异的**。 257 | 258 | 假如 A 不是方阵或者不是奇异的方阵,也可能有解,但是不能通过逆矩阵去求解。 259 | 260 | 261 | 262 | 263 | 264 | 265 | ### 1.1.6 向量和矩阵的范数归纳 266 | **向量的范数(norm)** 267 | 268 | 通常衡量向量的大小是通过**范数**来衡量的,形式上 $L^P$范数定义如下: 269 | 270 | $$ 271 | L_p=\Vert\vec{x}\Vert_p=\sqrt[p]{\sum_{i=1}^{N}|{x_i}|^p} 272 | $$ 273 | 274 | 这里 $p\ge 1$。 275 | 276 | 范数是将向量映射到非负数的函数,直观上来说,向量 x 的范数衡量从原点到点 x 的距离。 277 | 278 | 范数是满足下列性质的任意函数: 279 | $$ 280 | f(x)=0=>x=0 \\ 281 | f(x+y)\le f(x)+f(y)(三角不等式)\\ 282 | \forall \alpha \in R, f(\alpha x) = |\alpha|f(x) 283 | $$ 284 | 285 | 286 | 287 | 288 | 定义一个向量为:$\vec{a}=[-5, 6, 8, -10]$。任意一组向量设为$\vec{x}=(x_1,x_2,...,x_N)$。其不同范数求解如下: 289 | 290 | - 向量的1范数:向量的各个元素的绝对值之和,上述向量$\vec{a}$的1范数结果就是:x = |-5|+|6|+|8|+|-10| = 29。 291 | 292 | $$ 293 | \Vert\vec{x}\Vert_1=\sum_{i=1}^N\vert{x_i}\vert 294 | $$ 295 | 296 | - 向量的2范数(欧几里得范数):向量的每个元素的平方和再开平方根,上述$\vec{a}$的2范数结果就是:$x=\sqrt{(-5)^2+(6)^2+(8)^2+(-10)^2}15$。 297 | 298 | $$ 299 | \Vert\vec{x}\Vert_2=\sqrt{\sum_{i=1}^N{\vert{x_i}\vert}^2} 300 | $$ 301 | 302 | - 向量的负无穷范数:向量的所有元素的绝对值中最小的:上述向量$\vec{a}$的负无穷范数结果就是:5。 303 | 304 | $$ 305 | \Vert\vec{x}\Vert_{-\infty}=\min{|{x_i}|} 306 | $$ 307 | 308 | - 向量的正无穷范数:向量的所有元素的绝对值中最大的:上述向量$\vec{a}$的正无穷范数结果就是:10。 309 | 310 | $$ 311 | \Vert\vec{x}\Vert_{+\infty}=\max{|{x_i}|} 312 | $$ 313 | 314 | 315 | 316 | 317 | 318 | **矩阵的范数** 319 | 320 | 定义一个矩阵。 321 | $$ 322 | A = 323 | \left[ 324 | \begin{matrix} 325 | -1 & 2 & -3 \\ 326 | 4 & -6 & 6 \\ 327 | \end{matrix} 328 | \right] 329 | $$ 330 | 331 | 332 | 任意矩阵定义为:$A_{m\times n}$,其元素为 $a_{ij}$。 333 | 334 | 矩阵的范数定义为 335 | 336 | $$ 337 | \Vert{A}\Vert_p :=\sup_{x\neq 0}\frac{\Vert{Ax}\Vert_p}{\Vert{x}\Vert_p} 338 | $$ 339 | 340 | 当向量取不同范数时, 相应得到了不同的矩阵范数。 341 | 342 | - **矩阵的1范数(列范数)**:先对矩阵的每一列元素的绝对值求和,再从中取个最大的(列和最大),上述矩阵$A$的1范数先得到$[5,8,9]$,再取最大的最终结果就是:9。 343 | $$ 344 | \Vert A\Vert_1=\max_{1\le j\le n}\sum_{i=1}^m|{a_{ij}}| 345 | $$ 346 | 347 | - **矩阵的2范数**:矩阵$A^TA$的最大特征值开平方根,上述矩阵$A$的2范数得到的最终结果是:10.0623。 348 | 349 | $$ 350 | \Vert A\Vert_2=\sqrt{\lambda_{max}(A^T A)} 351 | $$ 352 | 353 | 其中, $\lambda_{max}(A^T A)$ 为 $A^T A$ 的特征值绝对值的最大值。 354 | - **矩阵的无穷范数(行范数)**:矩阵的每一行上的元素绝对值先求和,再从中取个最大的,(行和最大),上述矩阵$A$的行范数先得到$[6;16]$,再取最大的最终结果就是:16。 355 | $$ 356 | \Vert A\Vert_{\infty}=\max_{1\le i \le m}\sum_{j=1}^n |{a_{ij}}| 357 | $$ 358 | 359 | - **矩阵的核范数**:矩阵的奇异值(将矩阵svd分解)之和,这个范数可以用来低秩表示(因为最小化核范数,相当于最小化矩阵的秩——低秩),上述矩阵A最终结果就是:10.9287。 360 | 361 | - **矩阵的L0范数**:矩阵的非0元素的个数,通常用它来表示稀疏,L0范数越小0元素越多,也就越稀疏,上述矩阵$A$最终结果就是:6。 362 | - **矩阵的L1范数**:矩阵中的每个元素绝对值之和,它是L0范数的最优凸近似,因此它也可以表示稀疏,上述矩阵$A$最终结果就是:22。 363 | - **矩阵的F范数**:最常用的矩阵的范数,矩阵的各个元素平方之和再开平方根,它通常也叫做矩阵的L2范数,**它的优点在于它是一个凸函数,可以求导求解,易于计算**,上述矩阵A最终结果就是:10.0995。 364 | $$ 365 | \Vert A\Vert_F=\sqrt{(\sum_{i=1}^m\sum_{j=1}^n{| a_{ij}|}^2)} 366 | $$ 367 | 368 | - **矩阵的L21范数**:矩阵先以每一列为单位,求每一列的F范数(也可认为是向量的2范数),然后再将得到的结果求L1范数(也可认为是向量的1范数),很容易看出它是介于L1和L2之间的一种范数,上述矩阵$A$最终结果就是:17.1559。 369 | - **矩阵的 p范数** 370 | 371 | $$ 372 | \Vert A\Vert_p=\sqrt[p]{(\sum_{i=1}^m\sum_{j=1}^n{| a_{ij}|}^p)} 373 | $$ 374 | 375 | 两个向量的点积可以用范数来表示: 376 | $$ 377 | x^Ty =\Vert x \Vert_2 \Vert y \Vert_2 cos\theta 378 | $$ 379 | 这里 $\theta$ 就是 x 和 y 之间的夹角。 380 | 381 | 382 | 383 | ### 1.1.7 一些特殊的矩阵和向量 384 | 385 | **对角矩阵**:只在对角线上有非零元素,其他位置都是零。之前介绍的单位矩阵就是对角矩阵的一种; 386 | 387 | **对称矩阵**:转置和自己相等的矩阵,即:$A = A^T$。 388 | 389 | **单位向量**:具有单位范数的向量,也就是 $\Vert x \Vert_2 =1$ 390 | 391 | 392 | 393 | **向量正交**:如果 $x^Ty=0$,那么就说向量 x 和 y 互相正交。如果向量不仅互相正交,范数还是 1,那么就称为**标准正交**。 394 | 395 | 396 | 397 | **正交矩阵**:行向量和列向量是分别标准正交的方阵,即 398 | $$ 399 | A^TA=AA^T=I 400 | $$ 401 | 也就是有: 402 | $$ 403 | A^{-1}=A^T 404 | $$ 405 | 所以正交矩阵的一个优点就是求逆计算代价小。 406 | 407 | 408 | 409 | ### 1.1.8 如何判断一个矩阵为正定 410 | 411 | 判定一个矩阵是否为正定,通常有以下几个方面: 412 | 413 | - 顺序主子式全大于0; 414 | - 存在可逆矩阵$C$使$C^TC$等于该矩阵; 415 | - 正惯性指数等于$n$; 416 | - 合同于单位矩阵$E$(即:规范形为$E$) 417 | - 标准形中主对角元素全为正; 418 | - 特征值全为正; 419 | - 是某基的度量矩阵。 420 | 421 | 422 | 423 | 所有特征值是非负数的矩阵称为半正定,而所有特征值是负数的矩阵称为负定,所有特征值是非正数的矩阵称为半负定。 424 | 425 | **正定性的用途** 426 | 427 | - Hessian矩阵正定性在梯度下降的应用 428 | - 若Hessian正定,则函数的二阶偏导恒大于0,,函数的变化率处于递增状态,判断是否有局部最优解 429 | - 在 svm 中核函数构造的基本假设 430 | 431 | 432 | 433 | 434 | 435 | ## 1.2 特征值和特征向量 436 | 437 | ### 1.2.1 特征值分解与特征向量 438 | 439 | **特征分解是使用最广的矩阵分解之一**,矩阵分解可以得到一组特征值(eigenvalues)与特征向量(eigenvectors); 440 | 441 | 442 | 443 | 特征值表示的是这个特征到底有多重要,而特征向量表示这个特征是什么。 444 | 445 | 如果说一个向量$\vec{v}$是方阵$A$的特征向量,将一定可以表示成下面的形式: 446 | 447 | $$ 448 | A\nu = \lambda \nu 449 | $$ 450 | 451 | $\lambda$为特征向量$\vec{v}$对应的特征值。 452 | 453 | 特征值分解是将一个矩阵分解为如下形式: 454 | 455 | $$ 456 | A=Q\sum Q^{-1} 457 | $$ 458 | 459 | 其中,$Q$是这个矩阵$A$的**特征向量组成的正交矩阵**,$\sum$是一个对角矩阵,**每一个对角线元素就是一个特征值**,里面的特征值是由大到小排列的,这些特征值所对应的特征向量就是描述这个矩阵变化方向(从主要的变化到次要的变化排列)。也就是说矩阵$A$的信息可以由其特征值和特征向量表示。 460 | 461 | 并非每个矩阵都可以分解成特征值和特征向量,但**每个实对称矩阵**都可以分解为实特征向量和实特征值。 462 | 463 | 464 | 465 | ### 1.2.2 奇异值分解 466 | 467 | 除了特征分解外,还有一种矩阵分解,称为**奇异值分解**(SVD),将矩阵分解为奇异值和奇异向量。通过奇异值分解,可以得到和特征分解相同类型的信息,但是,奇异值分解有更广泛的应用,**每个实数矩阵都有一个奇异值分解,但不一定有特征分解,因为必须是方阵才有特征分解**。 468 | 469 | 在特征分解中,我们将 A 重新写作: 470 | $$ 471 | A = Vdiag(\lambda)V^{-1} 472 | $$ 473 | 其中,V 是特征向量构成的矩阵,$\lambda$是特征值构成的向量,$diag(\lambda)$表示一个对角线都是特征值的对角矩阵。 474 | 475 | 奇异值分解的形式如下所示: 476 | $$ 477 | A = U D V^T 478 | $$ 479 | 假如 A 是 $m\times n$ 的矩阵,则 U 是 $m\times m$的矩阵,D 是 $m\times n$ 的矩阵,V 是 $n\times n$ 的矩阵。并且,矩阵 U 和 V 是正交矩阵,D 是对角矩阵,且不一定是方阵。 480 | 481 | **D 对角线上的元素就是 A 的奇异值,而 U 的列向量是左奇异向量,V 的列向量是右奇异向量。** 482 | 483 | 可以套用和 A 相关的特征分解来解释其奇异值分解,A 的左奇异向量就是 $AA^T$的特征向量,而右奇异向量就是$A^TA$ 的特征向量,A 的非零奇异值是$AA^T$特征值的平方根,也是$A^TA$特征值的平方根。 484 | 485 | 486 | 487 | (来自深度学习 500 问的数学基础的内容) 488 | 489 | > 那么奇异值和特征值是怎么对应起来的呢?我们将一个矩阵$A$的转置乘以$A$,并对$A^TA$求特征值,则有下面的形式: 490 | > 491 | > $$ 492 | > (A^TA)V = \lambda V 493 | > $$ 494 | > 495 | > 这里$V$就是上面的右奇异向量,另外还有: 496 | > 497 | > $$ 498 | > \sigma_i = \sqrt{\lambda_i}, u_i=\frac{1}{\sigma_i}AV 499 | > $$ 500 | > 501 | > 这里的$\sigma$就是奇异值,$u$就是上面说的左奇异向量。 502 | 503 | 504 | 505 | 奇异值$\sigma$跟特征值类似,在矩阵$\sum$中也是从大到小排列,而且$\sigma$的减少特别的快,**在很多情况下,前10%甚至1%的奇异值的和就占了全部的奇异值之和的99%以上了**。也就是说,我们也可以用前$r$($r$远小于$m、n$)个的奇异值来近似描述矩阵,即部分奇异值分解: 506 | $$ 507 | A_{m\times n}\approx U_{m \times r}\sum_{r\times r}V_{r \times n}^T 508 | $$ 509 | 510 | 右边的三个矩阵相乘的结果将会是一个接近于$A$的矩阵,在这儿,**$r$越接近于$n$,则相乘的结果越接近于$A$**。 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | # 2. 概率论 519 | 520 | ## 2.1 概率分布与随机变量 521 | 522 | ### 2.1.1 机器学习为什么要使用概率 523 | 524 | 事件的概率是衡量该事件发生的可能性的量度。虽然在一次随机试验中某个事件的发生是带有偶然性的,但那些可在**相同条件下大量重复的随机试验**却往往呈现出**明显的数量规律**。 525 | 526 | 机器学习通常必须处理不确定量,有时候也需要处理随机量。几乎所有的活动都需要一些在不确定性存在的情况下进行推理的能力。 527 | 528 | 不确定性和随机性可能来自多个方面,不确定性有 3 种可能的来源: 529 | 530 | 1. **被建模系统内在的随机性**。比如纸牌游戏,假设纸牌被真正混洗成了随机顺序。 531 | 2. **不完全观测**。对于确定的系统,但是如果不能观测到所有驱动系统行为的变量时,该系统也会呈现随机性。比如让选手选择三扇门中的一个,并获得门后的奖品,每个门后的奖品是确定的,但是选手无法观测到,所以对于选手来说,结果是不确定的。 532 | 3. **不完全建模**。当采用一些必须舍弃某些信息的模型时,舍弃的信息可能导致模型的预测出现不确定性。 533 | 534 | 在很多情况下,采用简单而不确定的规则要比复杂而确定的规则更加的实用。 535 | 536 | 可以使用概率论来量化不确定性。 用概率来表示一种信任度,**概率直接和事件发生的频率相联系的被称为频率派概率**,比如说某件事发生的概率是 p,这表示如果反复试验无限次,有 p 的比例是发生这件事情;**而涉及确定性水平的称为贝叶斯概率**,比如说医生在对一个病人的诊断中判断其患某个病的概率是 p。 537 | 538 | 概率论在机器学习中扮演着一个核心角色,因为机器学习算法的设计通常依赖于对数据的概率假设。 539 | 540 | >例如在机器学习(Andrew Ng)的课中,会有一个朴素贝叶斯假设就是条件独立的一个例子。该学习算法对内容做出假设,用来分辨电子邮件是否为垃圾邮件。假设无论邮件是否为垃圾邮件,单词x出现在邮件中的概率条件独立于单词y。很明显这个假设不是不失一般性的,因为某些单词几乎总是同时出现。然而,最终结果是,这个简单的假设对结果的影响并不大,且无论如何都可以让我们快速判别垃圾邮件。 541 | 542 | 543 | 544 | ### 2.1.2 变量与随机变量有什么区别 545 | 546 | **随机变量**(random variable)是可以随机地取不同数值的变量。 547 | 548 | 它表示随机现象(在一定条件下,并不总是出现相同结果的现象称为随机现象)中各种结果的实值函数(一切可能的样本点)。例如某一时间内公共汽车站等车乘客人数,电话交换台在一定时间内收到的呼叫次数等,都是随机变量的实例。 549 | 随机变量与模糊变量的不确定性的本质差别在于,后者的测定结果仍具有不确定性,即模糊性。 550 | 551 | **变量与随机变量的区别:** 552 | 当变量的取值的概率不是1时,变量就变成了随机变量;当随机变量取值的概率为1时,随机变量就变成了变量。 553 | 554 | > 比如: 555 | > 当变量$x$值为100的概率为1的话,那么$x=100$就是确定了的,不会再有变化,除非有进一步运算. 556 | > 当变量$x$的值为100的概率不为1,比如为50的概率是0.5,为100的概率是0.5,那么这个变量就是会随不同条件而变化的,是随机变量,取到50或者100的概率都是0.5,即50%。 557 | 558 | 559 | 560 | ### 2.1.3 随机变量与概率分布的联系 561 | 562 | 一个随机变量仅仅表示一个可能取得的状态,还必须**给定与之相伴的概率分布**来制定每个状态的可能性。用来描述随机变量或一簇随机变量的每一个可能的状态的可能性大小的方法,就是概率分布(probability distribution)**. 563 | 564 | 随机变量可以分为离散型随机变量和连续型随机变量。 565 | 566 | 相应的描述其概率分布的函数是: 567 | 568 | - **概率质量函数**(Probability Mass Function, PMF):描述离散型随机变量的概率分布,通常用大写字母 $P$表示。 569 | 570 | - **概率密度函数**(Probability Density Function, PDF):描述连续型随机变量的概率分布,通常用小写字母$p$表示。 571 | 572 | 573 | 574 | ### 2.1.4 离散型随机变量和概率质量函数 575 | 576 | PMF 将随机变量能够取得的每个状态映射到**随机变量取得该状态的概率**。 577 | 578 | - 一般而言,$P(x)$ 表示时$ X=x$的概率,概率为 1 表示 $ X=x$ 是确定的,概率是 0 表示 $ X=x$ 是不可能的; 579 | - 有时候为了防止混淆,要明确写出随机变量的名称$P($x$=x)$ 580 | - 有时候需要先定义一个随机变量,然后制定它遵循的概率分布 x 服从$P($x$)$ 581 | 582 | PMF 可以同时作用于多个随机变量,即**联合概率分布**(joint probability distribution) $P(X=x,Y=y)$表示 $X=x$和$ Y=y$同时发生的概率,也可以简写成 $P(x,y)$. 583 | 584 | 如果一个函数$P$是随机变量 $X$ 的 PMF, 那么它必须满足如下三个条件: 585 | 586 | - $P$的定义域必须是的所有可能状态的集合 587 | - $∀x∈$x, $0 \leq P(x) \leq 1 $. 588 | - $∑_{x∈X} P(x)=1$. 我们把这一条性质称之为**归一化的**(normalized),如果不满足这条性质,那么可能某件事情发生的概率会是大于 1。 589 | 590 | 591 | 592 | 593 | 594 | ### 2.1..5 连续型随机变量和概率密度函数 595 | 596 | 如果一个函数$p$是x的PDF,那么它必须满足如下几个条件 597 | 598 | - $p$的定义域必须是x的所有可能状态的集合。 599 | - $∀x∈X,p(x)≥0$. 注意,我们并不要求$ p(x)≤1$,因为此处 $p(x)$不是表示的对应此状态具体的概率,而是概率的一个相对大小(密度)。具体的概率,需要积分去求。 600 | - $∫p(x)dx=1$, 积分下来,总和还是1,概率之和还是1. 601 | 602 | 注:PDF$p(x)$并没有直接对特定的状态给出概率,给出的是**密度**,相对的,它给出了落在面积为 $δx$的无线小的区域内的概率为$ p(x)δx$. 603 | 604 | 由此,我们无法求得具体某个状态的概率,我们可以求得的是 某个状态 $x$ 落在 某个区间$[a,b]$内的概率为$ \int_{a}^{b}p(x)dx$. 605 | 606 | 607 | 608 | ### 2.1.6 举例理解条件概率 609 | 610 | 条件概率公式如下: 611 | $$ 612 | P(A|B) = P(A\cap B) / P(B) 613 | $$ 614 | 说明:在同一个样本空间$\Omega$中的事件或者子集$A$与$B$,如果随机从$\Omega$中选出的一个元素属于$B$,那么下一个随机选择的元素属于$A$ 的概率就定义为在$B$的前提下$A$的条件概率。 615 | 616 | 条件概率文氏图示意如图1.1所示。 617 | ![](https://cai-images-1257823952.cos.ap-beijing.myqcloud.com/conditional_probability.jpg) 618 | 619 | 图1.1 条件概率文氏图示意 620 | 621 | 根据文氏图,可以很清楚地看到在事件B发生的情况下,事件A发生的概率就是$P(A\bigcap B)$除以$P(B)$。 622 | 623 | 举例:一对夫妻有两个小孩,已知其中一个是女孩,则另一个是女孩子的概率是多少?(面试、笔试都碰到过) 624 | 625 | **穷举法**:已知其中一个是女孩,那么样本空间为男女,女女,女男,则另外一个仍然是女生的概率就是1/3。 626 | 627 | **条件概率法**:$P(女|女)=P(女女)/P(女)$,夫妻有两个小孩,那么它的样本空间为女女,男女,女男,男男,则$P(女女)$为1/4,$P(女)= 1-P(男男)=3/4$,所以最后$1/3$。 628 | 629 | 这里大家可能会误解,男女和女男是同一种情况,但实际上类似姐弟和兄妹是不同情况。 630 | 631 | 632 | 633 | ### 2.1.7 联合概率与边缘概率联系区别 634 | 635 | **区别:** 636 | 联合概率:联合概率指类似于$P(X=a,Y=b)$这样,包含多个条件,且所有条件同时成立的概率。联合概率是指在多元的概率分布中**多个随机变量分别满足各自条件的概率**。 637 | 638 | 边缘概率:**边缘概率是某个事件发生的概率,而与其它事件无关**。边缘概率指类似于$P(X=a)$,$P(Y=b)$这样,仅与单个随机变量有关的概率。 639 | 640 | **联系:** 641 | 642 | 联合分布可求边缘分布,但若只知道边缘分布,无法求得联合分布。 643 | 644 | 645 | 646 | ### 2.1.8 条件概率的链式法则 647 | 648 | 由条件概率的定义,可直接得出下面的乘法公式: 649 | 乘法公式 设$A, B$是两个事件,并且$P(A) > 0$, 则有 650 | $$ 651 | P(AB) = P(B|A)P(A) 652 | $$ 653 | 推广 654 | $$ 655 | P(ABC)=P(C|AB)P(B|A)P(A) 656 | $$ 657 | 一般地,用归纳法可证:若$P(A_1A_2...A_n)>0$,则有 658 | $$ 659 | P(A_1A_2...A_n)=P(A_n|A_1A_2...A_{n-1})P(A_{n-1}|A_1A_2...A_{n-2})...P(A_2|A_1)P(A_1)\\ 660 | =P(A_1)\prod_{i=2}^{n}P(A_i|A_1A_2...A_{i-1}) 661 | $$ 662 | 任何多维随机变量联合概率分布,都可以分解成只有一个变量的条件概率相乘形式。 663 | 664 | 665 | 666 | ### 2.1.9 独立性和条件独立性 667 | 668 | **独立性** 669 | 两个随机变量$x$和$y$,概率分布可以表示成两个因子乘积形式,一个因子只包含$x$,另一个因子只包含$y$,则可以说这两个随机变量相互独立(independent)**。 670 | 条件有时为不独立的事件之间带来独立,有时也会把本来独立的事件,因为此条件的存在,而失去独立性。 671 | 672 | 673 | 举例:$P(XY)=P(X)P(Y)$, 事件$X$和事件$Y$独立。此时给定$Z$, 674 | $$ 675 | P(X,Y|Z) \not = P(X|Z)P(Y|Z) 676 | $$ 677 | **事件独立时,联合概率等于概率的乘积**。这是一个非常好的数学性质,然而不幸的是,无条件的独立是十分稀少的,因为大部分情况下,事件之间都是互相影响的。 678 | 679 | **条件独立性** 680 | 给定$Z$的情况下,$X$和$Y$条件独立,当且仅当 681 | $$ 682 | X\bot Y|Z \iff P(X,Y|Z) = P(X|Z)P(Y|Z) 683 | $$ 684 | $X$和$Y$的关系依赖于$Z$,而不是直接产生。 685 | 686 | > **举例**定义如下事件: 687 | > $X$:明天下雨; 688 | > $Y$:今天的地面是湿的; 689 | > $Z$:今天是否下雨; 690 | > $Z$事件的成立,对$X$和$Y$均有影响,然而,在$Z$事件成立的前提下,今天的地面情况对明天是否下雨没有影响。 691 | 692 | 693 | 694 | ### 2.1.10 常见公式 695 | 696 | **概率基础的公式** 697 | 698 | - $P(A+B) = P(A)+P(B)-P(AB)$ 699 | - $P(A-B)=P(A)-P(B)$ 700 | - $P(AB)=P(A)P(B|A)$ 701 | 702 | **全概率** 703 | 704 | $P(A) = \sum_i P(B_i)P(A|B_i)$ 705 | 706 | **贝叶斯** 707 | 708 | $P(B|A) = \frac{P(B)P(A|B)}{P(A)}$ 709 | 710 | 711 | 712 | ### 2.1.11 应用 713 | 714 | **抽球** 715 | 716 | n 个球,对于有放回和无放回的抽取方式 717 | 718 | - 有放回的抽取,抽取 m 个排成一列,求不同排列的数量:$n^m$ 719 | 720 | - 没有放回的抽取,抽取 m 个排成一列,求不同排列的数量:$\frac{n!}{(n-m)!}$ 721 | 722 | 723 | 724 | 725 | 726 | ## 2.2 常见概率分布 727 | 728 | ### 2.2.1 均匀分布 729 | 730 | 离散随机变量的均匀分布:假设 X 有 k 个取值,则均匀分布的概率质量函数为: 731 | $$ 732 | p(X=x_i) = \frac{1}{k},i=1,2,\cdots,k 733 | $$ 734 | 连续随机变量的均匀分布:假设 X 在 [a, b] 上均匀分布,则其概率密度函数为: 735 | $$ 736 | p(X=x) = 737 | 738 | \begin{cases} 739 | 0,x\notin[a,b]\\ 740 | \frac{1}{b-a},x\in[a, b] 741 | \end{cases} 742 | $$ 743 | 744 | 745 | ### 2.2.1 Bernoulli分布 746 | 747 | **Bernoulli分布**(伯努利分布,0-1分布)是单个二值随机变量分布, 单参数$\phi$∈[0,1]控制,$\phi$给出随机变量等于1的概率. 主要性质有: 748 | $$ 749 | \begin{align*} 750 | P(x=1) &= \phi \\ 751 | P(x=0) &= 1-\phi \\ 752 | 概率质量函数:P(x=x) &= \phi^x(1-\phi)^{1-x} \\ 753 | \end{align*} 754 | $$ 755 | 其期望和方差为: 756 | $$ 757 | \begin{align*} 758 | E_x[x] &= \phi \\ 759 | Var_x(x) &= \phi{(1-\phi)} 760 | \end{align*} 761 | $$ 762 | **适用范围**: **伯努利分布**适合对**离散型**随机变量建模. 763 | 764 | 765 | 766 | **Multinoulli分布**也叫**范畴分布**, 是单个*k*值随机分布,经常用来表示**对象分类的分布**. 其中$k$是有限值.Multinoulli分布由向量$\vec{p}\in[0,1]^{k-1}$参数化,每个分量$p_i$表示第$i$个状态的概率, 且$p_k=1-1^Tp$.这里$1^T$表示元素全为1的列向量的转置,其实就是对于向量p中除了k的概率之和。可以重写为$p_k=1-\sum_{0}^{k-1}p_i$ 。 767 | 768 | 补充二项分布、多项分布: 769 | 770 | 二项分布,通俗点硬币抛多次。二项分布(Binomial distribution)是**n重伯努利试验**成功次数的离散概率分布。 771 | 772 | 定义成功 x 次的概率为:$f(x)=C_n^xp^x(1-p)^{n-x},x\in{0,1,\cdots,n}$。 773 | 774 | **期望是 np, 方差是 np(1-p)** 775 | 776 | 多项式分布(Multinomial Distribution)是二项式分布的推广。二项式做n次伯努利实验,规定了每次试验的结果只有两个,如果现在还是做n次试验,只不过每次试验的结果可以有多m个,且m个结果发生的概率互斥且和为1,则发生其中一个结果X次的概率就是多项式分布。 777 | 778 | 779 | 780 | ### 2.2.3 高斯分布 781 | 782 | **高斯也叫正态分布**(Normal Distribution), 概率度函数如下: 783 | $$ 784 | N(x;\mu,\sigma^2) = \sqrt{\frac{1}{2\pi\sigma^2}}exp\left ( -\frac{1}{2\sigma^2}(x-\mu)^2 \right ) 785 | $$ 786 | 其中, $\mu$和$\sigma$分别是均值和标准差, 中心峰值x坐标由$\mu$给出, 峰的宽度受$\sigma$控制, 最大点在$x=\mu$处取得, 拐点为$x=\mu\pm\sigma$ 787 | 788 | 正态分布中,±1$\sigma$、±2$\sigma$、±3$\sigma$下的概率分别是68.3%、95.5%、99.73%,这3个数最好记住。 789 | 790 | 此外, 令$\mu=0,\sigma=1$高斯分布即简化为标准正态分布: 791 | $$ 792 | N(x;\mu,\sigma^2) = \sqrt{\frac{1}{2\pi}}exp\left ( -\frac{1}{2}x^2 \right ) 793 | $$ 794 | 对概率密度函数高效求值: 795 | $$ 796 | N(x;\mu,\beta^{-1})=\sqrt{\frac{\beta}{2\pi}}exp\left(-\frac{1}{2}\beta(x-\mu)^2\right) 797 | $$ 798 | 799 | 800 | 其中,$\beta=\frac{1}{\sigma^2}$通过参数$\beta∈(0,\infty)$来控制分布精度。 801 | 802 | 803 | 804 | ### 2.2.4 何时采用正态分布 805 | 806 | 问: 何时采用正态分布? 807 | 808 | 答: 缺乏实数上分布的先验知识, 不知选择何种形式时, **默认选择正态分布总是不会错的**, 理由如下: 809 | 810 | 1. 中心极限定理告诉我们, **很多独立随机变量均近似服从正态分布**, 现实中很多复杂系统都可以被建模成正态分布的噪声, 即使该系统可以被结构化分解. 811 | 2. 正态分布是具有相同方差的所有概率分布中, **不确定性最大的分布**, 换句话说, **正态分布是对模型加入先验知识最少的分布**. 812 | 813 | 814 | 815 | 正态分布的推广: 816 | 817 | 正态分布可以推广到$R^n$空间, 此时称为**多位正态分布**, 其参数是一个正定对称矩阵$\Sigma$: 818 | $$ 819 | N(x;\vec\mu,\Sigma)=\sqrt{\frac{1}{(2\pi)^ndet(\Sigma)}}exp\left(-\frac{1}{2}(\vec{x}-\vec{\mu})^T\Sigma^{-1}(\vec{x}-\vec{\mu})\right) 820 | $$ 821 | 对多为正态分布概率密度高效求值: 822 | $$ 823 | N(x;\vec{\mu},\vec\beta^{-1}) = \sqrt{det(\vec\beta)}{(2\pi)^n}exp\left(-\frac{1}{2}(\vec{x}-\vec\mu)^T\beta(\vec{x}-\vec\mu)\right) 824 | $$ 825 | 此处,$\vec\beta$是一个精度矩阵。 826 | 827 | 828 | 829 | ### 2.2.5 指数分布 830 | 831 | 深度学习中, 指数分布用来描述在$x=0$点处取得边界点的分布, 指数分布定义如下: 832 | $$ 833 | p(x;\lambda)=\lambda I_{x\geq 0}exp(-\lambda{x}) 834 | $$ 835 | 指数分布用指示函数$I_{x\geq 0}$来使$x$取负值时的概率为零。 836 | 837 | 838 | 839 | ### 2.2.6 Laplace 分布(拉普拉斯分布) 840 | 841 | 一个联系紧密的概率分布是 Laplace 分布(Laplace distribution),它允许我们在任意一点 $\mu$处设置概率质量的峰值 842 | $$ 843 | Laplace(x;\mu;\gamma)=\frac{1}{2\gamma}exp\left(-\frac{|x-\mu|}{\gamma}\right) 844 | $$ 845 | 846 | 期望是 $\mu$,方差是 $2\gamma^2$ 847 | 848 | 拉普拉斯分布比高斯分布更加尖锐和狭窄,在正则化中通常会利用这个性质。 849 | 850 | ### 2.2.7 泊松分布 851 | 852 | 假设已知事件在单位时间(或者单位面积)内发生的平均次数为λ,则泊松分布描述了:事件在单位时间(或者单位面积)内发生的具体次数为 k 的概率。 概率密度函数: 853 | $$ 854 | p(X=k;\lambda)=\frac{e^{-\lambda}\lambda^k}{k!} 855 | $$ 856 | 期望是 $\lambda$,方差是 $\lambda$. 857 | 858 | 859 | 860 | ### 2.2.8 Dirac分布和经验分布 861 | 862 | Dirac分布可保证概率分布中所有质量都集中在一个点上. Diract分布的狄拉克$\delta$函数(也称为**单位脉冲函数**)定义如下: 863 | $$ 864 | p(x)=\delta(x-\mu), x\neq \mu 865 | $$ 866 | 867 | $$ 868 | \int_{a}^{b}\delta(x-\mu)dx = 1, a < \mu < b 869 | $$ 870 | 871 | Dirac 分布经常作为**经验分布**(empirical distribution)的一个组成部分出现 872 | $$ 873 | \hat{p}(\vec{x})=\frac{1}{m}\sum_{i=1}^{m}\delta(\vec{x}-{\vec{x}}^{(i)}) 874 | $$ 875 | 其中, m个点$x^{1},...,x^{m}$是给定的数据集, **经验分布**将概率密度$\frac{1}{m}$赋给了这些点. 876 | 877 | 当我们在训练集上训练模型时, 可以认为从这个训练集上得到的经验分布指明了**采样来源**. 878 | 879 | **适用范围**: 狄拉克δ函数适合对**连续型**随机变量的经验分布. 880 | 881 | 关于经验分布的另一个重要观点是,它是训练数据的似然最大的那个概率密度函数。 882 | 883 | 884 | 885 | ### 2.2.9 混合分布 886 | 887 | 通过组合一些简单的概率分布来定义新的概率分布也是很常见的。 888 | 889 | 一种通用的组合方法就是**构造混合分布**。混合分布由一些组件分布构成。 890 | 891 | 一个混合分布的例子就是:实值变量的经验分布对于每一个训练实例来说,就是以 Dirac 分布为组件的混合分布。 892 | 893 | 894 | 895 | **混合模型**是组合简单概率分布来生成更丰富的一种简单策略。一个非常强大且常见的混合模型就是**高斯混合模型**。 896 | 897 | 它的组件是高斯分布,每个组件有自己的参数,均值和协方差矩阵。 898 | 899 | 900 | 901 | 902 | 903 | ## 2.3 期望、方差、协方差、相关系数 904 | ### 2.3.1 期望 905 | 906 | 函数 f(x) 关于某个分布 P(x) 的期望或者期望值是指,当 x 由 P 产生, f 作用于 x 的时候,f(x) 的平均值。 907 | 908 | 在概率论和统计学中,数学期望(或均值,亦简称期望)是试验中每次可能结果的概率乘以其结果的总和。**它反映随机变量平均取值的大小**。 909 | 910 | - 线性运算: $E(ax+by+c) = aE(x)+bE(y)+c$ 911 | - 推广形式: $E(\sum_{k=1}^{n}{a_ix_i+c}) = \sum_{k=1}^{n}{a_iE(x_i)+c}$ 912 | - 函数期望:设$f(x)$为$x$的函数,则$f(x)$的期望为 913 | - 离散函数: $E(f(x))=\sum_{k=1}^{n}{f(x_k)P(x_k)}$ 914 | - 连续函数: $E(f(x))=\int_{-\infty}^{+\infty}{f(x)p(x)dx}$ 915 | 916 | > 注意: 917 | > 918 | > - 函数的期望大于等于期望的函数(Jensen(詹森)不等式,即$E(f(x))\geqslant f(E(x))$ 919 | > - 一般情况下,乘积的期望不等于期望的乘积。 920 | > - 如果$X$和$Y$相互独立,则$E(xy)=E(x)E(y)$。 921 | 922 | 923 | 924 | ### 2.3.2 方差 925 | 926 | 概率论中方差用来**度量随机变量和其数学期望(即均值)之间的偏离程度**。方差是一种特殊的期望。定义为: 927 | 928 | $$ 929 | Var(x) = E((x-E(x))^2) 930 | $$ 931 | 932 | > 方差性质: 933 | > 934 | > 1)$Var(x) = E(x^2) -E(x)^2$ 935 | > 2)常数的方差为0; 936 | > 3)方差不满足线性性质; 937 | > 4)如果$X$和$Y$相互独立, $Var(ax+by)=a^2Var(x)+b^2Var(y)$ 938 | 939 | 940 | 941 | ### 2.3.3 协方差 942 | 943 | **协方差是衡量两个变量线性相关性强度及变量尺度**。 两个随机变量的协方差定义为: 944 | $$ 945 | Cov(x,y)=E((x-E(x))(y-E(y))) 946 | $$ 947 | 948 | 方差是一种特殊的协方差。当$X=Y$时,$Cov(x,y)=Var(x)=Var(y)$。 949 | 950 | > 协方差性质: 951 | > 952 | > 1)独立变量的协方差为0。 953 | > 2)协方差计算公式: 954 | 955 | $$ 956 | Cov(\sum_{i=1}^{m}{a_ix_i}, \sum_{j=1}^{m}{b_jy_j}) = \sum_{i=1}^{m} \sum_{j=1}^{m}{a_ib_jCov(x_iy_i)} 957 | $$ 958 | 959 | > 960 | >3)特殊情况: 961 | 962 | $$ 963 | Cov(a+bx, c+dy) = bdCov(x, y) 964 | $$ 965 | 966 | 967 | 968 | ### 2.3.4 相关系数 969 | 970 | **相关系数是研究变量之间线性相关程度的量**。两个随机变量的相关系数定义为: 971 | $$ 972 | Corr(x,y) = \frac{Cov(x,y)}{\sqrt{Var(x)Var(y)}} 973 | $$ 974 | 975 | > 相关系数的性质: 976 | > 1)有界性。相关系数的取值范围是 [-1,1],可以看成无量纲的协方差。 977 | > 2)值越接近1,说明两个变量正相关性(线性)越强。越接近-1,说明负相关性越强,当为0时,表示两个变量没有相关性。 978 | 979 | 980 | 981 | ## 2.4 信息论 982 | 983 | 信息论主要研究的是对一个信号包含新的多少进行量化。 984 | 985 | 信息论的一个基本想法是一个不太可能发生的事件居然发生了,比一个非常可能发生的事件发生,能提供更多的信息。 986 | 987 | 如果想通过这种基本想法来量化信息,需要满足这个 3 个性质: 988 | 989 | - 非常可能发生的事件信息论要比较少,并且极端情况下,确保能够发生的事件应该没有信息量; 990 | - 较不可能发生的事件具有更高的信息量; 991 | - 独立事件应具有增量的信息。例如,投掷的硬币两次正面朝上传递的信息,应该是投掷一次硬币证明朝上的信息量的两倍。 992 | 993 | 这里定义一个事件 x=$x$ 的**自信息**为: 994 | $$ 995 | I(x) = -log P(x) 996 | $$ 997 | 自信息量只能处理单个的输出。可以用**香农熵**来对整个概率分布中的不确定性总量进行量化: 998 | $$ 999 | H(x) = -E_{x\sim P}[I(x)] = -E_{x\sim P}[logP(x)] 1000 | $$ 1001 | 也记作 H(P)。这里的 E 表示的就是期望,也就是说一个分布的香农熵是指遵循这个分布的事件所产生的**期望信息总量**。 1002 | 1003 | 而如果对于一个随机变量有两个单独的概率分布 P(x) 和 Q(x),那么可以使用**KL 散度**来衡量这两个分布的差异: 1004 | $$ 1005 | D_{KL}(P||Q) = E_{x\sim P}[\frac{logP(x)}{logQ(x)}] = E_{x\sim P}[logP(x)-logQ(x)] 1006 | $$ 1007 | 举例:对于一个二值随机分布的香农熵,$H(x) =- (1-p)log(1-p)-plogp$ 1008 | 1009 | KL散度的性质有: 1010 | 1011 | 1. 非负的; 1012 | 2. KL 散度为 0 的情况,当且仅当 P 和 Q 在离散型变量的情况下是相同的分布,或者在连续型变量的情况下是“几乎处处”相同的; 1013 | 3. 常用作衡量分布之间的某种距离,但并不是真正的距离,因为它不是对称的。 1014 | 1015 | 一个和 KL 散度很相似的是**交叉熵**,即 $H(P,Q)=H(P)+D_{KL}(P||Q)$: 1016 | $$ 1017 | H(P,Q)=-E_{x\sim P}logQ(x) 1018 | $$ 1019 | 针对 Q 最小化交叉熵等价于最小化 KL 散度,因为 Q 并不参与被省略的那一项。 1020 | 1021 | 在计算这些量的时候,经常会遇到 0log0 这个表达式,一般对这个的处理是 $lim_{x->0}xlogx = 0$ 1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | # 3. 数值计算 1032 | 1033 | ## 3.1 上溢和下溢 1034 | 1035 | 连续数学在计算机上的根本困难是:**需要通过有限数量的位模式来表示无限多的实数**。 1036 | 1037 | 这意味着在计算机中表示实数的时候几乎总是会引入一些近似误差,许多情况下这仅仅是**舍入误差**。但如果没有考虑最小化舍入误差的累积,可能就会导致算法的失效。这里存在两种比较严重的情况: 1038 | 1039 | 1. **下溢**:当接近 0 的数被四舍五入为零时发生下溢的情况。许多函数在参数为 0 而不是一个很小的正数的时候会表现出质的不同。例如,我们通常要避免被零除(有些软件会返回异常,或者会抛出一个非数字,比如 NaN 的占位符)或者避免取零的对数(这通常被视为负无穷) 1040 | 2. **上溢**:当大量级的数被近似为 $\infin$ 或者 $-\infin$ 。进一步的运算会导致这些无限制变为非数字,比如 inf 1041 | 1042 | 必须对上溢和下溢进行数值稳定的一个例子是 **softmax 函数**,它经常用于预测和 Multinoulli 分布相关联的概率,也是 CNN 里常用的函数,其定义如下: 1043 | $$ 1044 | softmax(x)_i = \frac{exp(x_i)}{\sum^n_{j=1}exp(x_j)} 1045 | $$ 1046 | 假如 x 是等于一个常数 c,那么输出应该是 $\frac{1}{n}$ ,但有两种情况需要注意: 1047 | 1048 | 1. c 是一个很小的负数,会发生下溢的情况; 1049 | 2. c 是一个很大的正数,会发现上溢的情况 1050 | 1051 | 解决的办法,是先做一个简单的变换: 1052 | $$ 1053 | z = x-max_i(x_i) 1054 | $$ 1055 | 然后计算 softmax(z) 。 1056 | 1057 | 也就是先让每个 x 减去最大值,那么这让exp 的参数最大值为 0(当 x 取最大值的时候),这排除了上溢的可能性;而分母中至少有一项是 1(x 取最大值,那么 exp(z)=1),这也排除了下溢的可能性,即分母不会等于0. 1058 | 1059 | 但这里还有另一个问题就是分子的下溢导致结果是 0,这种情况如果计算 log softmax(x) 会得到$-\infin$ 的结果。 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | 1067 | ## 3.2 导数和偏导数 1068 | 1069 | ### 3.2.1 导数和偏导计算 1070 | 1071 | **导数定义**: 1072 | 1073 | 导数(derivative)代表了在自变量变化趋于无穷小的时候,函数值的变化与自变量的变化的比值。 1074 | 1075 | - 几何意义是这个点的切线。 1076 | - 物理意义是**该时刻的(瞬时)变化率**。 1077 | 1078 | *注意*:在一元函数中,只有一个自变量变动,也就是说只存在一个方向的变化率,这也就是为什么一元函数没有偏导数的原因。在物理学中有平均速度和瞬时速度之说。平均速度有 1079 | $$ 1080 | v=\frac{s}{t} 1081 | $$ 1082 | 1083 | 其中$v$表示平均速度,$s$表示路程,$t$表示时间。这个公式可以改写为 1084 | 1085 | $$ 1086 | \bar{v}=\frac{\Delta s}{\Delta t}=\frac{s(t_0+\Delta t)-s(t_0)}{\Delta t} 1087 | $$ 1088 | 1089 | 其中$\Delta s$表示两点之间的距离,而$\Delta t$表示走过这段距离需要花费的时间。当$\Delta t$趋向于0($\Delta t \to 0$)时,也就是时间变得很短时,平均速度也就变成了在$t_0$时刻的瞬时速度,表示成如下形式: 1090 | 1091 | $$ 1092 | v(t_0)=\lim_{\Delta t \to 0}{\bar{v}}=\lim_{\Delta t \to 0}{\frac{\Delta s}{\Delta t}}=\lim_{\Delta t \to 0}{\frac{s(t_0+\Delta t)-s(t_0)}{\Delta t}} 1093 | $$ 1094 | 1095 | 实际上,上式表示的是路程$s$关于时间$t$的函数在$t=t_0$处的导数。一般的,这样定义导数:如果平均变化率的极限存在,即有 1096 | 1097 | $$ 1098 | \lim_{\Delta x \to 0}{\frac{\Delta y}{\Delta x}}=\lim_{\Delta x \to 0}{\frac{f(x_0+\Delta x)-f(x_0)}{\Delta x}} 1099 | $$ 1100 | 1101 | 则称此极限为函数 $y=f(x)$ 在点 $x_0$ 处的导数。记作 $f'(x_0)$ 或 $y'\vert_{x=x_0}$ 或 $\frac{dy}{dx}\vert_{x=x_0}$ 或 $\frac{df(x)}{dx}\vert_{x=x_0}$。 1102 | 1103 | 通俗地说,导数就是曲线在某一点切线的斜率。 1104 | 1105 | 1106 | 1107 | **偏导数**: 1108 | 1109 | 既然谈到偏导数(partial derivative),**那就至少涉及到两个自变量**。以两个自变量为例,$z=f(x,y)$,从导数到偏导数,也就是从曲线来到了曲面。曲线上的一点,其切线只有一条。但是曲面上的一点,切线有无数条。而偏导数就是指多元函数沿着坐标轴的变化率。 1110 | 1111 | 1112 | *注意*:直观地说,偏导数也就是函数在某一点上沿坐标轴正方向的的变化率。 1113 | 1114 | 设函数$z=f(x,y)$在点$(x_0,y_0)$的领域内有定义,当$y=y_0$时,$z$可以看作关于$x$的一元函数$f(x,y_0)$,若该一元函数在$x=x_0$处可导,即有 1115 | 1116 | $$ 1117 | \lim_{\Delta x \to 0}{\frac{f(x_0+\Delta x,y_0)-f(x_0,y_0)}{\Delta x}}=A 1118 | $$ 1119 | 1120 | 函数的极限$A$存在。那么称$A$为函数$z=f(x,y)$在点$(x_0,y_0)$处关于自变量$x$的偏导数,记作$f_x(x_0,y_0)$或$\frac{\partial z}{\partial x}\vert_{y=y_0}^{x=x_0}$或$\frac{\partial f}{\partial x}\vert_{y=y_0}^{x=x_0}$或$z_x\vert_{y=y_0}^{x=x_0}$。 1121 | 1122 | 偏导数在求解时可以将另外一个变量看做常数,利用普通的求导方式求解,比如$z=3x^2+xy$关于$x$的偏导数就为$z_x=6x+y$,这个时候$y$相当于$x$的系数。 1123 | 1124 | 某点$(x_0,y_0)$处的偏导数的几何意义为曲面$z=f(x,y)$与面$x=x_0$或面$y=y_0$交线在$y=y_0$或$x=x_0$处切线的斜率。 1125 | 1126 | 1127 | 1128 | ### 3.2.2 导数和偏导数有什么区别? 1129 | 1130 | 导数和偏导没有本质区别,如果极限存在,都是当自变量的变化量趋于0时,函数值的变化量与自变量变化量比值的极限。 1131 | 1132 | > - 一元函数,一个$y$对应一个$x$,导数只有一个。 1133 | > - 二元函数,一个$z$对应一个$x$和一个$y$,有两个导数:一个是$z$对$x$的导数,一个是$z$对$y$的导数,称之为偏导。 1134 | > - 求偏导时要注意,对一个变量求导,则视另一个变量为常数,只对改变量求导,从而将偏导的求解转化成了一元函数的求导。 1135 | 1136 | 1137 | 1138 | ### 3.2.3 导数的四则运算 1139 | 1140 | 1141 | 1142 | - $(u+v)^{'}=u^{'}+v^{'}$ 1143 | - $(u-v)^{'}=u^{'}-v^{'}$ 1144 | - $(uv)^{'} = u^{'}v+uv^{'}$ 1145 | - $(u/v)^{'}=\frac{u^{'}v-uv^{'}}{v^2}$ 1146 | 1147 | 1148 | 1149 | ### 3.2.4 常见的导数运算法则 1150 | 1151 | - y=c(常数) ==> $y^{'}=0$ 1152 | - $y=x^a$ ==> $y^{'}=ax^{a-1}$ 1153 | - $y=a^x$ ==> $y^{'}=ln(a)a^x$ 1154 | - $y=log_ax$==>$y^{'} = \frac{1}{xlna}$ 1155 | - $y = lnx$ ==> $y^{'}=\frac{1}{x}$ 1156 | - $y=sin(x)$ ==> $y^{'}=cos(x)$ 1157 | - $y=cos(x)$ ==> $y^{'}=-sin(x)$ 1158 | - $y=tan(x)$ ==> $y^{'}=\frac{1}{cos(x)^2}$ 1159 | 1160 | 1161 | 1162 | ### 3.2.5 复合函数的运算法则 1163 | 1164 | 假设 g 在 x 处可导,且 f 在 g(x) 处可导,那么 y=f(g(x)) 的导数计算: 1165 | $$ 1166 | y^{'} = f^{'}(g(x))\cdot g^{'}(x) 1167 | $$ 1168 | 1169 | 1170 | 1171 | 1172 | 1173 | 1174 | ## 3.3 基于梯度的优化方法 1175 | 1176 | 大多数深度学习算法都涉及某种形式的优化。**优化指的是改变 x 以最小化或者最大化某个函数 f(x) 的任务**。 1177 | 1178 | 通常我们都用最小化 f(x) 指代大多数最优化问题,最大化可以由最小化 -f(x) 来实现。 1179 | 1180 | 一般将需要优化的函数称为**目标函数(object function)或者准则(criterion)**,而如果是其进行最小化的时候,也称为**代价函数(cost function)、损失函数(loss function)或者误差函数(error function)**。 1181 | 1182 | 一般用一个上标 * 表示最小化或者最大化函数的 x 的取值,比如 $x^* = argmin f(x)$。 1183 | 1184 | 假设有一个函数 y=f(x),其导数记作 $f^{'}(x)$或者 $\frac{dy}{dx}$ 1185 | 1186 | 导数对于最小化是非常有用的,因为它告诉我们如何更改 x 来略微地改善 y。我们可以根据导数来决定对 x 的修改,是增大 x 还是减小 x,一般是移动 x 来走到导数为 0 的位置,从而最小化 y,这种做法就是**梯度下降**。 1187 | 1188 | 对于 $f^{'}=0$的点,称为**临界点或者驻点**。这个点可能是局部的极小点或者极大点,也可能是全局最小点或者最大点,也可能是一个鞍点,也就是既不是最大点也不是最小点。 1189 | 1190 | 理想情况下,当然是希望达到全局极小点,但这可能达不到,通常是达到局部极小点,但有的局部极小点非常接近全局最小点,有的极小点可能非常大,所以要尽量达到全局极小点或者接近的局部极小点。 1191 | 1192 | 1193 | 1194 | **梯度**是相对一个向量求导的导数,f 的导数是包含所有偏导数的向量,记为 $\nabla_xf(x)$,梯度的第 i 个元素是 f 关于 $x_i$ 的偏导数。在多维情况下,临界点是梯度中所有元素都是 0 的点。 1195 | 1196 | 在负梯度上移动可以减少 f,这称为**最速下降法或者梯度下降**,它建议新的点为: 1197 | $$ 1198 | x^{'}=x-\alpha \nabla_x f(x) 1199 | $$ 1200 | 这里 $\alpha$ 是学习率,是一个确定步长的正标量,通常其初始值是一个比较小的常数。 1201 | 1202 | 1203 | 1204 | 1205 | 1206 | # 参考文献 1207 | 1208 | 1. Ian,Goodfellow,Yoshua,Bengio,Aaron...深度学习[M],人民邮电出版,2017 1209 | 2. 深度学习 500 问第一章数学基础:https://github.com/scutan90/DeepLearning-500-questions/tree/master/ch01_%E6%95%B0%E5%AD%A6%E5%9F%BA%E7%A1%80 1210 | 3. [Reflection_Summary--数学](https://github.com/sladesha/Reflection_Summary/tree/master/%E6%95%B0%E5%AD%A6) 1211 | 1212 | 1213 | 1214 | 1215 | 1216 | -------------------------------------------------------------------------------- /Notes/机器学习基础.md: -------------------------------------------------------------------------------- 1 | 机器学习基础的学习笔记。 2 | 3 | 4 | 5 | # 1. 基本概念 6 | 7 | ## 1.1 定义 8 | 9 | 机器学习算法是一种能够从数据中学习的算法。那么这里的学习的定义是什么呢?这里有一个简单的定义: 10 | 11 | > 对于某类任务 T 和性能度量 P,一个计算机程序通过经验 E 改进后,在任务 T 上由性能度量 P 衡量的性能有所提升,这称为学习。 12 | 13 | 举例来说这个定义,比如对于图像分类这个任务,一般的性能度量 P 就是分类的准确率,而经验 E 其实就是图片数据集,当我们采用的算法,比如 CNN,在给定的训练集上训练后,然后在测试集上的准确率有所提升,这就是学习了。 14 | 15 | 这里的任务 T、经验 E 和性能 P 其实指代的内容非常的多,这里简单的介绍一下。 16 | 17 | 首先,对于任务 T,在机器学习领域里,可以是这些方向的任务: 18 | 19 | - 分类:在该任务中计算机程序需要判断输入数据是属于给的 k 类中的哪一类,最常见的就是人脸识别,也是图像分类的一个子方向,另外还有语音识别、文本识别等; 20 | - 回归:在该任务中需要对给定的输入预测数值,比如预测房价或者证券未来的价格等; 21 | - 转录:将一些相对非结构化表示的数据,转录为离散的文本形式。比如 OCR(光学字符识别)、语音识别等; 22 | - 机器翻译:将一种语言的序列转化为另一种语言。比如英语翻译为中文; 23 | - 异常检测:查找不正常或者非典型的个体; 24 | - 去噪 25 | 26 | - 等等 27 | 28 | 对于性能度量 P,在不同的任务中会采用不同的性能指标,比如: 29 | 30 | - 准确率和错误率 31 | - 召回率、精准率、F1 32 | - ROC 和 AUC 33 | - 均方误差(MSE)、均方根误差(RMSE) 34 | - 交并比 IoU 35 | 36 | 而经验 E,一般就是指数据集了,不同的任务对数据集的要求也不一样,比如图片分类一般就是图片和图片的标签,但目标检测、图像分割,需要的除了图片、标签,有的还需要图片中物体的标注框或者坐标信息等。 37 | 38 | 39 | 40 | ## 1.2 各种常见算法图示 41 | 42 | 如下图所示,这些是常见的机器学习算法的图示。 43 | 44 | | 回归算法 | 聚类算法 | 正则化方法 | 45 | | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | 46 | | | | | 47 | 48 | | 决策树学习 | 贝叶斯方法 | 基于核的算法 | 49 | | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | 50 | | | | | 51 | 52 | | 聚类算法 | 关联规则学习 | 人工神经网络 | 53 | | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | 54 | | | | | 55 | 56 | | 深度学习 | 降低维度算法 | 集成算法 | 57 | | :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | 58 | | | | | 59 | 60 | 61 | 62 | ## 1.3 计算图的导数计算 63 | 64 | 计算图导数计算是反向传播,利用链式法则和隐式函数求导。 65 | 66 | 假设 $z = f(u,v)$ 在点 $(u,v)$ 处偏导连续,$(u,v)$是关于 $t$ 的函数,在 $t$ 点可导,求 $z$ 在 $t$ 点的导数。 67 | 68 | 根据链式法则有 69 | $$ 70 | \frac{dz}{dt}=\frac{\partial z}{\partial u}.\frac{du}{dt}+\frac{\partial z}{\partial v} 71 | .\frac{dv}{dt} 72 | $$ 73 | 74 | 链式法则用文字描述:“由两个函数凑起来的复合函数,其导数等于里边函数代入外边函数的值之导数,乘以里边函数的导数。 75 | 76 | 为了便于理解,下面举例说明: 77 | $$ 78 | f(x)=x^2,g(x)=2x+1 79 | $$ 80 | 81 | 则: 82 | $$ 83 | {f[g(x)]}'=2[g(x)] \times g'(x)=2[2x+1] \times 2=8x+4 84 | $$ 85 | 86 | ## 1.4 局部最优和全局最优 87 | 88 | 优化问题一般分为局部最优和全局最优。其中, 89 | 90 | 1. 局部最优,就是在函数值空间的一个**有限区域内寻找最小值**;而全局最优,是在函数值空间**整个区域寻找最小值**问题。 91 | 2. 函数局部最小点是它的函数值小于或等于附近点的点,但是有可能大于较远距离的点。 92 | 3. 全局最小点是那种它的函数值小于或等于所有的可行点。 93 | 94 | 95 | 96 | ### 1.4.1 如何区分局部最小点和鞍点 97 | 98 | 参考知乎回答: 99 | 100 | - https://www.zhihu.com/question/358632429/answer/919562000 101 | 102 | - https://www.zhihu.com/question/68109802/answer/263503269 103 | 104 | 105 | 106 | 通常一阶导数为 0 的点称为稳定点,可以分为三类: 107 | 108 | - 局部最小点 109 | - 局部最大点 110 | - 鞍点 111 | 112 | 113 | 114 | 鞍点如下所示: 115 | 116 | 117 | 118 | 一般区分鞍点和局部最优的方法是使用神经网络 loss surface 的 Hessian 矩阵,通过计算 Hessian 矩阵的特征值,进行判断: 119 | 120 | - 当 Hessian 矩阵的**特征值有正有负**的时候,神经网络的一阶导数为 0 的点是**鞍点**; 121 | - 当 Hessian 矩阵的**特征值是非负**的时候,神经网络的一阶导数为 0 的点是**局部极小值点**; 122 | - 当 Hessian 矩阵**最小特征值小于零**,则为**严格鞍点**(包含了局部最大) 123 | 124 | 125 | 126 | 根据文章:[Geometry of Neural Network Loss Surfaces via Random Matrix Theory](https://link.zhihu.com/?target=http%3A//proceedings.mlr.press/v70/pennington17a/pennington17a.pdf),可以看到神经网络的 Hessian 矩阵的特征值分布如下: 127 | 128 | 129 | 130 | 其中 $\phi$ 表示参数数目和数据量之比,其值越大表示数量相对较少,$\lambda$ 是特征值,$\epsilon$ 表示 loss 值,所以从上图可以得到: 131 | 132 | - 当 loss 很大的时候,特征值有正有负,**表明鞍点是困扰优化的主要原因**; 133 | - 当 loss 很小的时候,特征值慢慢都是非负数,**也就是说这个时候基本是局部最小点**。 134 | 135 | 136 | 137 | 另外一种判断是否是鞍点的方法:**若某个一阶导数为0的点在至少一个方向上的二阶导数小于0,那它就是鞍点**。 138 | 139 | 最优点和鞍点的区别在于**其在各个维度是否都是最低点**。 140 | 141 | 只要某个一阶导数为0的点在某个维度上是最高点而不是最低点,那它就是鞍点。**而区分最高点和最低点当然就是用二阶导数**,斜率从负变正的过程当然就是“下凸”,即斜率的导数大于0,即二阶导数大于0。反之则为“上凹”,二阶导数小于0。 142 | 143 | 144 | 145 | 146 | 147 | ### 1.4.2 如何避免陷入局部最小值或者鞍点 148 | 149 | 实际上,我们并不需要害怕陷入局部最小值,原因有这几个: 150 | 151 | - 第一个,很直观的解释来自于上面特征值的分布信息:当loss很小的时候,我们才会遇到局部最小值问题,**也就是说这时候的loss已经足够小,我们对这时候的loss已经足够满意了,不太需要花更大力气去找全局最优值**。 152 | 153 | - 第二个,**在一定假设条件下,很多研究表明深度学习中局部最小值很接近于全局最小值**。 154 | 155 | 另外,根据https://www.zhihu.com/question/68109802的回答: 156 | 157 | > 实际上我们可能并没有找到过”局部最优“,更别说全局最优了; 158 | > 159 | > ”局部最优是神经网络优化的主要难点“,这其实是来自于一维优化问题的直观想象,单变量的情况下,优化问题最直观的困难就是有很多局部极值。但在多变量的情况下,就不一定能找到局部最优了; 160 | 161 | 162 | 163 | 164 | 165 | 而对于鞍点,逃离鞍点的做法有这几种: 166 | 167 | 1. 利用严格鞍点负特征值对应的方向,采用矩阵向量乘积的形式找到下降方向; 168 | 2. 利用扰动梯度方法逃离鞍点,在梯度的模小于某个数的时候,在梯度上加个动量。 169 | 170 | 171 | 172 | 173 | 174 | ## 1.5 机器学习、深度学习、数据挖掘、大数据之间的关系 175 | 176 | 首先来看这四者简单的定义: 177 | 178 | - **大数据**通常被定义为“超出常用软件工具捕获,管理和处理能力”的数据集,一般是在数据量、数据速度和数据类别三个维度上都大的问题。 179 | - **机器学习**关心的问题是如何构建计算机程序使用经验自动改进。 180 | - **数据挖掘**是从数据中提取模式的特定算法的应用,在数据挖掘中,重点在于算法的应用,而不是算法本身。 181 | - **深度学习**是机器学习的一个子类,一般特指学习层数较高的网络结构,这个结构通常会结合线性和非线性的关系。 182 | 183 | 关于这四个的关系,可以如下图所示: 184 | 185 | 186 | 187 | 188 | 189 | **机器学习和数据挖掘**之间的关系如下: 190 | 191 | > 数据挖掘是一个过程,在此过程中机器学习算法被用作提取数据集中的潜在有价值模式的工具。 192 | 193 | 194 | 195 | 大数据与深度学习关系总结如下: 196 | 197 | (1)**深度学习是一种模拟大脑的行为**。可以从所学习对象的机制以及行为等等很多相关联的方面进行学习,模仿类型行为以及思维。 198 | 199 | (2)**深度学习对于大数据的发展有帮助**。深度学习对于大数据技术开发的每一个阶段均有帮助,不管是数据的分析还是挖掘还是建模,只有深度学习,这些工作才会有可能一一得到实现。 200 | 201 | (3)**深度学习转变了解决问题的思维**。很多时候发现问题到解决问题,走一步看一步不是一个主要的解决问题的方式了,在深度学习的基础上,要求我们从开始到最后都要基于一个目标,为了需要优化的那个最终目标去进行处理数据以及将数据放入到数据应用平台上去,这就是端到端(End to End)。 202 | 203 | (4)**大数据的深度学习需要一个框架**。在大数据方面的深度学习都是从基础的角度出发的,深度学习需要一个框架或者一个系统。总而言之,将你的大数据通过深度分析变为现实,这就是深度学习和大数据的最直接关系。 204 | 205 | 206 | 207 | 机器学习和深度学习的关系: 208 | 209 | - 深度学习是机器学习的一个子类,是机器学习的一类算法,相比传统的机器学习方法,深度学习有这几个特点: 210 | - **对硬件要求更高**。经常需要 GPU 才能快速完成任务,单纯用 CPU 执行任务,那耗时是非常的惊人; 211 | - **对数据量要求更高**。传统的机器学习一般可能只需要几百上千的数据量,但是对于深度学习的任务,至少也需要上万甚至几百万数据量,否则很容易过拟合; 212 | - **具有更强的特征提取能力**。深度学习可以从数据中学习到不同等级的特征,从低级的边缘特征,到高级的语义特征,这也是越来越多的机器学习方向都采用深度学习算法来解决问题的一个原因,性能更加强; 213 | - **可解释性差**。因为抽象层次较高,所以深度学习也经常被称为是一个黑匣子。 214 | - 机器学习的核心是数学,是用一个数学模型,然后输入数据来调节数学模型的参数,从而让数学模型可以解决特定的某类问题。简单说就是希望训练得到一个可以解决特定问题的数学函数。 215 | 216 | 217 | 218 | ## 1.6 为什么要使用机器学习 219 | 220 | 原因如下: 221 | 222 | - 需要进行大量手工调整或需要拥有长串规则才能解决的问题:机器学习算法通常可以**简化代码、提高性能**。 223 | - 问题复杂,传统方法难以解决:最好的机器学习方法可以找到解决方案。 224 | - 环境有波动:机器学习算法可以**适应新数据**。 225 | - 洞察复杂问题和大量数据 226 | 227 | 228 | 一些机器学习的应用例子: 229 | 230 | - 数据挖掘 231 | - 一些无法通过手动编程来编写的应用:如自然语言处理,计算机视觉 232 | - 一些自助式的程序:如推荐系统 233 | - 理解人类是如何学习的 234 | 235 | 236 | 237 | 238 | 239 | # 2. 机器学习系统的类型 240 | 241 | 机器学习有多种类型,可以根据如下规则进行分类: 242 | 243 | - 是否在人类监督下进行训练(监督,非监督,半监督和强化学习) 244 | - 是否可以动态渐进学习(在线学习 vs批量学习) 245 | - 它们是否只是通过简单地比较新的数据点和已知的数据点,或者在训练数据中进行模式识别,以建立一个预测模型,就像科学家所做的那样(基于实例学习 vs基于模型学习) 246 | 247 | 248 | 249 | ## 2.1 是否有监督 250 | 251 | 第一种分类机器学习的方法是可以根据训练时监督的量和类型进行分类。主要有四类:监督学习、非监督学习、半监督学习和强化学习。 252 | 253 | ### 2.1.1 监督学习 254 | 255 | 监督学习,顾名思义就是带有监督的学习,**而监督就是体现在训练数据都是有标签的**,所有在训练模型的时候可以根据数据的真实标签不断调整模型,从而得到一个性能更好的模型。 256 | 257 | 监督学习主要有两个常见的典型的任务--**分类和回归**。 258 | 259 | #### 分类 260 | 261 | 分类问题主要就是预测新数据的类别问题。例如上文提到的垃圾邮件过滤器就是一个二分类问题,将邮件分为垃圾邮件还是正常的邮件,如下图所示。 262 | 263 | ![](https://gitee.com/lcai013/image_cdn/raw/master/notes_images/%E5%88%86%E7%B1%BB%E7%A4%BA%E4%BE%8B.png) 264 | 265 | 266 | 267 | 268 | 269 | #### 回归 270 | 271 | 回归问题主要是预测目标数值。比如给定预测房价的问题,给定一些特征,如房子大小、房间数量、地理位置等等,然后预测房子的价格。如下图所示: 272 | 273 | ![](https://gitee.com/lcai013/image_cdn/raw/master/notes_images/%E5%9B%9E%E5%BD%92%E7%A4%BA%E4%BE%8B.png) 274 | 275 | 注意,一些回归算法也可以用来进行分类,反之亦然。例如,逻辑回归通常用来进行分类,它可以生成一属于每个类别的概率值,然后选择最大概率的类别作为预测的类别。 276 | 277 | 常用的监督学习算法有: 278 | 279 | - K近邻算法 280 | - 线性回归 281 | - 逻辑回归 282 | - 支持向量机(SVM) 283 | - 决策树和随机森林 284 | - 深度学习方法 285 | 286 | 287 | 288 | ### 2.1.2 非监督学习 289 | 290 | 和监督学习相反,非监督学习就是采用没有标签的数据集。 291 | 292 | 非监督主要有四个典型的任务,分别是聚类、降维、异常检测和关联规则学习。 293 | 294 | 295 | 296 | #### 聚类 297 | 298 | 聚类就是将数据根据一定的规则分成多个类,通常是采用相似性。比如对于博客访客的聚类,通过聚类算法,检测相似性访客的分组,如下图所示。不需要告诉算法访客是哪个类别,它会自动根据访客的属性找到相互间的关系,比如它可能找出访客的职业关系,将访客分为有 40% 的是上班族,有 50% 的是学生,或者对于技术博客,可能就是根据开发方向,划分为前端、后台、移动开发、人工智能等等。甚至,如果采用层次聚类分析算法,还可以继续对上述的分类进行更加详细的划分。这种做法可以帮助博主知道自己博客的主要群体是谁,更好规划自己博客发表的文章应该以什么方向为主。 299 | 300 | ![](https://gitee.com/lcai013/image_cdn/raw/master/notes_images/cluster_example.png) 301 | 302 | 可视化算法也是极佳的非监督学习案例:**给算法大量复杂的且不加标签的数据,算法输出数据的2D或3D图像**。如下图所示,算法会试图保留数据的结构(即尝试保留输入的独立聚类,避免在图像中重叠),这样就可以明白数据是如何组织起来的,也许还能发现隐藏的规律。 303 | 304 | ![](https://gitee.com/lcai013/image_cdn/raw/master/notes_images/visualize_example.png) 305 | 306 | #### 降维 307 | 308 | **降维的目的是简化数据、但是不能失去大部分信息**。做法之一是**合并若干相关的特征**。例如,汽车的里程数与车龄高度相关,降维算法就会将它们合并成一个,表示汽车的磨损。这叫做特征提取。 309 | 310 | 此外,在采用机器学习算法训练的时候,可以对训练集进行降维,这样有助于提高训练速度,降低占用的硬盘和内存空间,有时候也能提高算法的性能,但必须选择合适的降维算法,否则性能实际上是很有可能会下降的。 311 | 312 | 313 | 314 | #### 异常检测 315 | 316 | 另一个重要的非监督任务是异常检测(anomaly detection)。例如,检测异常的信用卡转账以防欺诈,检测制造缺陷,或者在训练之前自动从训练数据集去除异常值。异常检测的系统使用正常值训练的,当它碰到一个新实例,它可以判断这个新实例是像正常值还是异常值。 317 | 318 | ![](https://gitee.com/lcai013/image_cdn/raw/master/notes_images/anomaly%20detection.png) 319 | 320 | #### 关联规则学习 321 | 322 | 最后,另一个常见的非监督任务是关联规则学习,它的目标是挖掘大量数据以发现属性间有趣的关系。例如,假设你拥有一个超市。在销售日志上运行关联规则,可能发现买了烧烤酱和薯片的人也会买牛排。因此,你可以将这些商品放在一起。 323 | 324 | 325 | 326 | 327 | 下面是一些最重要的非监督学习算法: 328 | 329 | 1. 聚类 330 | - K 均值(k-means) 331 | - 层次聚类分析(Hierarchical Cluster Analysis, HCA) 332 | - 期望最大值 333 | 2. 可视化和降维 334 | - 主成分分析(Principal Component Analysis, PCA) 335 | - 核主成分分析 336 | - 局部线性嵌入(Locally-Linear Embedding, LLE) 337 | - t-分布邻域嵌入算法(t-distributed Stochastic Neighbor Embedding, t-SNE) 338 | 3. 关联性规则学习 339 | - Apriori 算法 340 | - Eclat算法 341 | 342 | 343 | 344 | 345 | 346 | ### 2.1.3 半监督学习 347 | 348 | 一些算法可以处理部分带标签的训练数据,通常是大量不带标签数据加上小部分带标签数据。这称作半监督学习。如下图所示,图中灰色圆点表示没有标签的数据,仅有几个三角形和正方形点表示带标签的数据。 349 | 350 | ![](https://gitee.com/lcai013/image_cdn/raw/master/notes_images/semi-supriviesed_learning.png) 351 | 352 | **多数半监督学习算法是非监督和监督算法的结合**。 353 | 354 | 例如,深度信念网络(deep belief networks)是基于被称为互相叠加的受限玻尔兹曼机(restricted Boltzmann machines,RBM)的非监督组件。RBM 是先用非监督方法进行训练,再用监督学习方法进行整个系统微调。 355 | 356 | 半监督学习的示例,如一些图片存储服务,比如 Google Photos,是半监督学习的好例子。一旦你上传了所有家庭相片,它就能自动识别相同的人 A 出现了相片1、5、11 中,另一个人 B 出现在了相片 2、5、7 中。这是算法的非监督部分(聚类)。现在系统需要的就是你告诉这两个人是谁。只要给每个人一个标签,算法就可以命名每张照片中的每个人,特别适合搜索照片。 357 | 358 | 359 | 360 | 常见应用场景:应用场景包括分类和回归,算法包括一些对常用监督式学习算法的延伸,通过对已标记数据建模,在此基础上,对未标记数据进行预测。 361 | 362 | 363 | 364 | 算法举例:常见算法如图论推理算法(Graph Inference)或者拉普拉斯支持向量机(Laplacian SVM)等。 365 | 366 | 367 | 368 | 369 | 370 | ### 2.1.4 强化学习 371 | 372 | 强化学习和上述三种学习问题是非常不同的。学习系统在这里被称为**智能体**( agent),可以对环境进行观察,选择和执行动作,获得**奖励**(负奖励是惩罚,见下图)。然后它必须自己学习哪个是最佳方法(称为**策略**,policy),以得到长久的最大奖励。策略决定了智能体在给定情况下应该采取的行动 。 373 | 374 | ![](https://gitee.com/lcai013/image_cdn/raw/master/notes_images/Reinforcement_learning.png) 375 | 376 | 377 | 378 | 目前强化学习的应用还不算非常广,特别是结合了深度学习的强化学习,主要是应用在机器人方面,当然最著名的一个应用就是 DeepMind 的 AlphaGo 了,它是通过分析数百万盘棋局学习制胜策略,然后自己和自己下棋。要注意,在比赛中机器学习是关闭的;AlphaGo 只是使用它学会的策略。 379 | 380 | 381 | 382 | ## 2.2 是否可以动态渐进学习 383 | 384 | 第二种分类机器学习的准则是,它是否能从导入的数据流进行持续学习。也就是如果导入的是持续的数据流,机器学习算法能否在不断采用新数据来训练已经训练好的模型,并且新的模型对新旧数据都还有很好的性能。 385 | 386 | 387 | 388 | ### 2.2.1 批量学习 389 | 390 | 在批量学习中,**系统不能进行持续学习:必须用所有可用数据进行训练**。这通常会占用大量时间和计算资源,所以一般是线下做的。 391 | 392 | 首先是进行训练,然后部署在生产环境且停止学习,它只是使用已经学到的策略。**这称为离线学习**。 393 | 394 | 对于批量学习算法来说,当获取到新数据的时候,就需要重新重头训练整个数据集,然后更新模型,如果是应用该算法系统,那就相当于需要更新系统,需要停掉旧版本的系统,重新上线新版本的系统。 395 | 396 | 当然,一般训练、评估、部署一套机器学习的系统的整个过程可以自动进行,所以即便是批量学习也可以适应改变。只要有需要,就可以方便地更新数据、训练一个新版本。并且对于更新周期,可以选择每 24 小时或者每周更新一次。 397 | 398 | 但是,批量学习还是存在下面的缺点: 399 | 400 | 1. **实时性差**,即对于需要快速适应变化的系统,比如预测股票变化、电商推荐系统等,就不适合采用批量学习算法; 401 | 2. **耗费大量计算资源**,用全部数据训练需要大量计算资源(CPU、内存空间、磁盘空间、磁盘 I/O、网络 I/O 等等),特别是训练集特别大的情况,更加凸显这个问题的严峻性; 402 | 3. **无法应用在资源有限的设备上**,比如需要自动学习的系统,但是如果采用智能手机,每次采用大量训练数据重新训练几个小时是非常不实际的。 403 | 404 | 405 | 406 | ### 2.2.2 在线学习 407 | 408 | 批量学习的缺陷和问题可以通过采用在线学习算法来解决。 409 | 410 | 在在线学习中,是用数据实例持续地进行训练,可以一次一个或一次几个实例(称为小批量)。每个学习步骤都很快且廉价,所以系统可以动态地学习到达的新数据。 411 | 412 | 在线学习虽然名字带着在线两个字,但是实际上它的训练过程也是离线的,因此应该说是持续学习或者增量学习。 413 | 414 | 在线学习有下面几个优点: 415 | 416 | 1. **实时性好**。在线学习算法非常适合接收连续流的数据,然后自动更新模型,实时性比批量学习更好; 417 | 2. **可以节省大量计算资源**。在线学习算法在学习新数据后,可以扔掉训练数据,从而节省大量存储空间;此外,训练得过程不需要加载所有训练数据,对于内存、CPU 等资源的要求也大大减少; 418 | 3. **实现核外学习**(out-of-core learning)。当内存不足以加载训练集的时候,可以采用在线学习算法多次训练,每次加载一部分训练集,即将一部分训练集当做新数据不断加载,直到训练完所有数据。 419 | 420 | 在线学习也存在两个挑战: 421 | 422 | 1. **学习速率问题**。学习速率是在线学习的一个重要参数,它反映了在线学习算法有多快地适应数据的改变,必须选择一个合适的学习速率,因为学习速率过大,系统可以很快适应新数据,但是也容易遗忘旧数据,比如图像分类问题,训练了一个 50 类分类器后,增加新的 10 类数据,一旦学习速率过快,系统只会记住新的 10 个类别,忘记了前面的 50 个类别的数据。相反的,如果你设定的学习速率低,系统的惰性就会强:即,它学的更慢,但对新数据中的噪声或没有代表性的数据点结果不那么敏感。 423 | 2. **坏数据的影响**。如果采用坏数据训练,会破坏系统的性能。要减小这种风险,你需要密集监测,如果检测到性能下降,要快速关闭(或是滚回到一个之前的状态)。你可能还要监测输入数据,对反常数据做出反应(比如,使用异常检测算法)。 424 | 425 | 426 | 427 | ## 2.3 实例学习 vs 模型学习 428 | 429 | 第三种分类机器学习的方法是判断它们是如何进行归纳推广的。大多机器学习任务是关于预测的。这意味着给定一定数量的训练样本,系统需要能推广到之前没见到过的样本。对训练数据集有很好的性能还不够,真正的目标是对新实例预测的性能。 430 | 431 | 有两种主要的归纳方法:基于实例学习和基于模型学习。 432 | 433 | ### 2.3.1 实例学习 434 | 435 | 基于实例学习是**系统先用记忆学习案例,然后使用相似度测量推广到新的例子**,如下图所示: 436 | 437 | ![](https://gitee.com/lcai013/image_cdn/raw/master/notes_images/BaseOnInstanceLearning.png) 438 | 439 | 这种学习算法可以说是机器学习中最简单的算法了,**它实际上就是采用存储的数据集进行分类或者回归**。 440 | 441 | **典型的算法就是 KNN 算法**,即 K 近邻算法,它就是将新的输入数据和已经保存的训练数据采用相似性度量(一般采用欧式距离)得到最近的 K 个训练样本,并采用 K 个训练样本中类别出现次数最多的类别作为预测的结果。 442 | 443 | 所以,这种算法的缺点就比较明显了: 444 | 445 | - **对存储空间的需求很大**,需要占用的空间直接取决于实例数量的大小; 446 | - **运行时间比较慢**,因为需要需要与已知的实例进行比对。 447 | 448 | 449 | 450 | ### 2.3.2 模型学习 451 | 452 | 和基于实例学习相反的就是基于模型学习:建立这些样本的模型,然后使用这个模型进行预测。如下图所示: 453 | 454 | ![](https://gitee.com/lcai013/image_cdn/raw/master/notes_images/BaseOnModelLearning.png) 455 | 456 | 457 | 基于模型学习算法的流程一般如下所示: 458 | 459 | - 研究数据。先对数据进行分析,这可能包含清洗数据、特征筛选、特征组合等等 460 | - 选择模型。选择合适的模型,从简单的线性回归、逻辑回归,到慢慢复杂的随机森林、集成学习,甚至深度学习的卷积神经网络模型等等 461 | - 用训练数据进行训练。也就是寻找最适合算法模型的参数,使得代价函数取得最小值。 462 | - 使用模型对新案例进行预测(这称作推断)。预测结果非常好,就能上线系统;如果不好,就需要进行错误分析,问题出现在哪里,是数据问题还是模型问题,找到问题,然后继续重复这个流程。 463 | 464 | 465 | 466 | 467 | 468 | # 3. 如何构建一个机器学习项目 469 | 470 | 一般我们构建一个机器学习项目,常用的还是监督学习的算法,所以这里按照监督学习算法来简单说明如何构造一个机器学习项目。 471 | 472 | 通常的步骤是这样的: 473 | 474 | 1. **项目概述**。明确项目的目标,需要用什么性能度量,然后确定问题的类别(监督学习、非监督学习或者需要批量学习还是在线学习方法),大概确定采用的算法; 475 | 2. **获取数据**。获取公开的相关领域的数据集,准备开发环境,测试集一般就是采用特定的数据集,比如业务给出的实际应用场景的数据。 476 | 3. **发现并可视化数据,发现规律**。对数据进行探索,查找数据间存在的一些关联和规律。 477 | 4. **为机器学习算法准备数据**。这一步其实就是做好特征工程,对原始数据进行预处理、特征提取、特征选择等步骤。 478 | 5. **模型训练**。选择好合适的机器学习模型,然后进行训练,通常可能会考虑选择多个模型进行对比,然后挑选合适的算法模型。 479 | 6. **微调模型**。对选择好的模型进行调参,尽量得到最佳的性能。 480 | 7. **给出解决方案**。这一步主要是展示给你的上级,你要采用什么算法模型,实验结果如何等等。 481 | 8. **部署、监控、维护系统**。最后就是部署模型,上线,然后监控服务的运行,并进行维护。 482 | 483 | 484 | 485 | # 4. 机器学习的主要挑战 486 | 487 | 在介绍基于模型学习算法的流程的时候,对于预测结果不好的问题分析,主要说了是数据问题还是模型问题,这同时也就是机器学习的效果不好的两个主要原因,即错误的数据和错误的算法。 488 | 489 | ## 4.1 训练数据量不足 490 | 491 | 第一个问题就是**训练数据的数量问题**,这是非常重要的问题。 492 | 493 | 因为即使是简单的问题,一般也需要数千的样本,这还是因为简单的问题一般采用简单的算法就可以解决,对于复杂的图像或语音问题,通常需要数百万的样本,特别是如果采用现在非常热门的深度学习算法,比如卷积神经网络模型,这些复杂的模型如果没有足够的数据量支持,非常容易陷入过拟合的情况。 494 | 495 | 实际上更多数量的训练集也是为了获得更有代表性的数据,能够学习到这类数据的所有特征。 496 | 497 | 但是,应该注意到,**小型和中型的数据集仍然是非常常见的,获得额外的训练数据并不总是轻易和廉价的,所以不要抛弃算法**。 498 | 499 | 500 | 501 | ## 4.2 没有代表性的训练数据 502 | 503 | 无论采用基于实例还是基于模型的学习,让训练数据对新数据具有代表性是非常重要的。如果训练集没有代表性,那么训练得到的模型就是不可能得到准确性的模型,比如人脸识别中,模型没有学习到某个人最明显的代表性的特征,比如高鼻梁或者没有眉毛等突出特征,那么模型对这个人的识别率就不会很高。 504 | 505 | 使用具有代表性的训练集对于推广到新案例是非常重要的。但是做起来比说起来要难:**如果样本太小,就会有样本噪声(即会有一定概率包含没有代表性的数据),但是即使是非常大的样本也可能没有代表性,如果取样方法错误的话。这叫做样本偏差。** 506 | 507 | 508 | 509 | 510 | ## 4.3 低质量的数据 511 | 512 | 低质量的数据指的是**数据有错误、带有过多噪声或者是出现异常值等的数据,这种数据会影响系统整体的性能**,因此,**数据清洗**对于构建一个机器学习系统或者一个机器学习项目来说都是必不可少的步骤。 513 | 514 | 对于这些低质量的数据,通常可以按照如下做法处理: 515 | 516 | - 如果一些实例是明显的异常值,最好删掉它们或尝试手工修改错误; 517 | - 如果一些实例缺少特征(比如,你的 5% 的顾客没有说明年龄),你必须决定是否忽略这个属性、忽略这些实例、填入缺失值(比如,年龄中位数),或者训练一个含有这个特征的模型和一个不含有这个特征的模型,等等。 518 | 519 | 520 | 521 | ## 4.4 不相关的特征 522 | 523 | 不相关的特征对于整个机器学习系统是有着反作用的效果,训练数据必须包含足够多的相关特征且非相关特征不多的情况下,才能训练出一个性能不错的模型。机器学习项目成功的关键之一是用好的特征进行训练。这个过程称作**特征工程**,包括: 524 | 525 | - 特征选择:在所有存在的特征中选取最有用的特征进行训练。 526 | - 特征提取:组合存在的特征,生成一个更有用的特征(如前面看到的,可以使用降维算法)。 527 | - 收集新数据创建新特征。 528 | 529 | 530 | 531 | ## 4.5 过拟合 532 | 533 | 上述四种情况都是坏数据的情况,接下来是两种算法问题,也是机器学习最常见的两种算法方面的问题,**过拟合和欠拟合**。 534 | 535 | **过拟合就是指算法模型在训练集上的性能非常好,但是泛化能力很差,即在测试集上的效果却很糟糕的情况**。比如下图,采用一个高阶多项式回归模型来预测生活满意度和人均 GDP 的关系,很明显看出来,这个模型过拟合了训练数据,其预测效果并不会达到在训练数据上这么好的效果。 536 | 537 | ![](https://gitee.com/lcai013/image_cdn/raw/master/notes_images/OverfittingExample.png) 538 | 539 | 通常对于比较复杂的模型,比如深度神经网络,它能够检测和识别到数据中比较细微的规律和特征,但是如果训练集包含噪声,或者训练集数量太少(数量太少会引入样本噪声),这种情况下,模型同样会学习这种噪声,从而导致模型的泛化能力的下降。 540 | 541 | 一般解决过拟合的方法有: 542 | 543 | - 简化模型,这包括了采用简单点的模型、减少特征数量以及限制模型,即采用正则化; 544 | - 增加训练数据 545 | - 减小训练数据的噪声,即数据清洗,比如修正数据错误和去除异常值等 546 | 547 | 其中**正则化方法是比较常用的方法,它的作用就是限制模型,不让模型过于复杂,从而降低过拟合的风险或者是缓和过拟合的程度**。常用的正则化方法是 **L2 和 L1 正则化**。正则化方法通常会采用一个超参数来控制其限制模型的强度。超参数是一个学习算法的参数(而不是模型的)。这样,它是不会被学习算法本身影响的,它优于训练,在训练中是保持不变的。如何调节超参数也是构建一个机器学习算法模型非常重要的一个步骤,也是让性能能够进一步提升的做法。 548 | 549 | 550 | 551 | ## 4.6 欠拟合 552 | 553 | 欠拟合和过拟合刚好相反,**它就是模型的性能非常差,在训练数据和测试数据上的性能都不好。** 554 | 555 | 通常也是因为模型过于简单,没有能够很好学习到数据的有效的相关的特征,解决方法有: 556 | 557 | - 选择一个更强大的模型,带有更多参数 558 | - 用更好的特征训练学习算法(特征工程) 559 | - 减小对模型的限制(比如,减小正则化超参数) 560 | 561 | 562 | 563 | # 5. 常用的分类算法优缺点 564 | 565 | 这里给出常用的分类算法的优缺点: 566 | 567 | | 算法 | 优点 | 缺点 | 568 | | :-------------------------: | :----------------------------------------------------------- | :----------------------------------------------------------- | 569 | | 线性回归 | 1. 结果易于理解;
2. 容易实现,计算不复杂 | 对非线性数据的拟合效果不好 | 570 | | Logistic Regression逻辑回归 | 1. 速度快。
2. 简单易于理解,直接看到各个特征的权重。
3. 能容易地更新模型吸收新的数据。
4. 如果想要一个概率框架,动态调整分类阀值。
5. 实现简单,广泛应用于工业问题上 | 1. 特征处理复杂。
2. 只能处理二分类问题(需要 softmax 才可以处理多分类),且必须线性可分。
3. 容易欠拟合,一般准确率不太高。
4. 不能很好处理大量多类特征或者变量。
5. 特征空间很大时,性能一般。 | 571 | | Decision Tree决策树 | 1. 不需要任何领域知识或参数假设。
2. 适合高维数据。
3. 可解释性强,简单易于理解。
4. 效率高,短时间内可以处理大量数据,得到可行且效果较好的结果。
5. 能够同时处理数据型和常规性属性。 | 1. 对于各类别样本数量不一致数据,信息增益偏向于那些具有更多数值的特征。
2. 易于过拟合。
3. 忽略属性之间的相关性。
4. 不支持在线学习。
5. 单棵决策树分类能力弱,并且对连续值变量难以处理。 | 572 | | 随机森林 RandomForest | 1. 在数据集上表现良好,在当前的很多数据集上,相对其他算法有着很大的优势。
2. 它能够处理很高维度(特征很多)的数据,并且不用做特征选择。
3. 可以评估特征的重要性
4. 在创建随机森林的时候,对 generlization error 使用的是无偏估计
5. 训练速度快,容易做成并行化方法
6. 在训练过程中,能够检测到特征间的互相影响
7. 实现比较简单
8. 对于不平衡的数据集来说,它可以平衡误差
9. 可以应用在特征缺失的数据集上,并仍然有不错的性能 | 1. 随机森林已经被证明在某些**噪音较大**的分类或回归问题上会过拟合。
2. 对于有不同取值的属性的数据,**取值划分较多的属性会对随机森林产生更大的影响**,所以随机森林在这种数据上产出的属性权值是不可信的。 | 573 | | SVM支持向量机 | 1. 可以解决小样本下机器学习的问题。
2. 提高泛化性能。
3. 可以解决高维、非线性问题。超高维文本分类仍受欢迎。
4. 避免神经网络结构选择和局部极小的问题。 | 1. 对缺失数据或者噪音敏感。
2. 内存消耗大,难以解释。
3. 运行和调参略烦人。
4. 对大规模数据训练比较困难。
5. 无法直接支持多分类,但可以通过间接的方法来实现。 | 574 | | Bayes 贝叶斯分类法 | 1. 所需估计的参数少,对于缺失数据不敏感。
2.有着坚实的数学基础,以及稳定的分类效率。 | 1. 需要假设属性之间相互独立,这往往并不成立。(喜欢吃番茄、鸡蛋,却不喜欢吃番茄炒蛋)。
2. 需要知道先验概率。
3. 分类决策存在错误率。 | 575 | | KNN K近邻 | 1. 思想简单,理论成熟,既可以用来做分类也可以用来做回归;
2. 可用于非线性分类;
3.训练时间复杂度为O(n);
4. 准确度高,对数据没有假设,对outlier不敏感; | 1. 计算量太大。
2. 对于样本分类不均衡的问题,会产生误判。
3. 需要大量的内存。
4. 输出的可解释性不强。 | 576 | | Neural Network 神经网络 | 1. 分类准确率高。
2. 并行处理能力强。
3. 分布式存储和学习能力强。
4. 鲁棒性较强,不易受噪声影响。 | 1. 需要大量参数(网络拓扑、阀值、阈值)。
2. 结果难以解释。
3. 训练时间过长。 | 577 | | Adaboosting | 1. adaboost是一种有很高精度的分类器。
2. 可以使用各种方法构建子分类器,Adaboost算法提供的是框架。
3. 当使用简单分类器时,计算出的结果是可以理解的。而且弱分类器构造极其简单。
4. 简单,不用做特征筛选。
5. 不用担心overfitting。 | 对outlier比较敏感 | 578 | | GDBT | 1. 精度高。
2. 能处理非线性数据和多特征类型数据。
3. 适合低维稠密数据。
4. 可解释性好。
5. 不需要对特征做归一化,可以自动选择特征。
6.能采用多少损失函数,比如均方误差和 LogLoss 等 | 1. boosting 是个串行过程,并行比较麻烦,需要考虑上下树之间的关系。
2. 计算复杂度高。
3. 不适合高位稀疏数据。 | 579 | 580 | 581 | 582 | # 6. 代价函数 583 | 584 | 585 | 586 | 587 | 588 | # 7. 损失函数 589 | 590 | 591 | 592 | 593 | 594 | # 8. 梯度下降 595 | 596 | 597 | 598 | 599 | 600 | # 9. 模型评估 601 | 602 | 603 | 604 | 605 | 606 | # 10. 逻辑回归 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | # 参考 623 | 624 | 1. 《深度学习》 625 | 2. 深度学习 500 问:https://github.com/scutan90/DeepLearning-500-questions 626 | 3. https://www.zhihu.com/question/358632429/answer/919562000 627 | 4. https://www.zhihu.com/question/68109802/answer/263503269 628 | 5. 《hands-on-ml-with-sklearn-and-tf》 629 | 6. Andrew Ng 在 Coursea 上的[机器学习课程](https://www.coursera.org/learn/machine-learning) 630 | 631 | ### 632 | 633 | -------------------------------------------------------------------------------- /Notes/目标检测学习笔记.md: -------------------------------------------------------------------------------- 1 | # 目标检测学习笔记 2 | 3 | ## 1. 目标定位(object localization) 4 | 5 | 图像分类、目标定位以及检测的区别如下图所示,前两个是图片中只有 1 个对象的情况,而检测是图片有多个对象的情况。 6 | 7 | ![](https://cai-images-1257823952.cos.ap-beijing.myqcloud.com/%E7%9B%AE%E6%A0%87%E5%AE%9A%E4%BD%8D.png) 8 | 9 | 所以,目标定位实际上是在分类的基础上定位到对象的位置,即找到对象在哪里并且直到这个对象是属于哪一类。 10 | 11 | 在图像分类中,一般定义的标签 y 的维度和类别是一样的,即假如是有 3 个类别,那么标签 y 的维度也是 3 个,比如令 $y=[c_1, c_2, c_3]$ ,然后输出的时候就判断哪个类别的预测概率大,就将其作为该对象的预测类别。 12 | 13 | 而在目标定位中,增加了寻找对象的位置的工作,那么标签就需要有坐标信息,所以这里假设是 3 个类别,定义其标签为: 14 | $$ 15 | y = \ \begin{bmatrix}p_c \\ b_x \\ b_y \\ b_h \\ b_w \\ c_1 \\ c_2 \\ c_3 \\\end{bmatrix} 16 | $$ 17 | 其中,$p_c$ 表示图片是否包含有对象,如果对象属于指定的3 个类别中的一个,那么$p_c=1$, 否则就是 $p_c=0$,然后接下来的 $b_x, b_y, b_h, b_w$ 表示的就是坐标,或者说就是边界框参数,一般来说就是左上角的坐标加上边界框的宽和高,然后最后 3 个就是代表类别了,有多少个类别,就有多少个参数,其数值表示的预测概率。 18 | 19 | 然后神经网络的损失函数,一般就是采用平方误差策略,假设类别 $y$ 和网络的输出 $\hat{y}$,那么损失函数就是这么计算了,根据上述的标签定义,是有 9 维的: 20 | $$ 21 | L(\hat{y}, y) = (\hat{y_1}-y_1)^2+(\hat{y_2}-y_2)^2+\dots +(\hat{y_8}-y_8)^2 22 | $$ 23 | 当然了,这里是用平方误差简化了,实际应用中,通常做法是对边界框的坐标应用平方差或者类似方法,对 $p_c$ 应用逻辑回归函数,或者评分预测误差,而对类别标签应用对数损失函数。 24 | 25 | 26 | 27 | ## 2. 基于滑动窗口的目标检测算法 28 | 29 | 对于基于滑动窗口的目标检测算法,首先是创建一个标签训练集,也就是将图片进行剪切成多个图片样本,如下图所示,将左图进行剪切,得到中间的 5个样本,然后按照样本是否包含汽车,进行标注标签,然后将这个训练集输入 CNN 中,训练得到一个模型。 30 | 31 | ![](https://cai-images-1257823952.cos.ap-beijing.myqcloud.com/%E5%9F%BA%E4%BA%8E%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E7%9A%84%E7%9B%AE%E6%A0%87%E6%A3%80%E6%B5%8B.png) 32 | 33 | 当训练好模型后,就可以进行测试,测试的例子如下所示,选择一个特定大小的窗口,然后从图片左上角开始滑动,每次将窗口内的图片送入模型中,判断该图片内是否有汽车,依次重复操作,直到滑动窗口滑过测试图片的每个角落。 34 | 35 | ![](https://cai-images-1257823952.cos.ap-beijing.myqcloud.com/%E5%9F%BA%E4%BA%8E%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E7%9A%84%E7%9B%AE%E6%A0%87%E6%A3%80%E6%B5%8B%202.png) 36 | 37 | 上述做法就是**滑动窗口目标检测**。以某个步幅滑动这些方框窗口遍历整张图片,对这些方形区域进行分类,判断是否包含目标对象。 38 | 39 | **该算法的一个很明显的缺点,就是计算成本**。主要原因是跟滑动窗口的大小有关系,选择太小的,那么就会需要滑动很多次,也就是需要检测多个小窗口,提高了计算成本;而如果窗口过大,那么窗口数量会减少,但是会影响模型的性能。 40 | 41 | ### 滑动窗口的卷积实现 42 | 43 | 上述介绍的实现基于滑动窗口的目标检测的方法,效率是比较低,这里会介绍如何通过卷积实现滑动窗口,首先需要将 CNN 的全连接层转换为卷积层,也就是得到一个全卷积网络(FCN),如下图所示: 44 | 45 | ![](https://cai-images-1257823952.cos.ap-beijing.myqcloud.com/%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E7%9A%84%E5%8D%B7%E7%A7%AF%E5%AE%9E%E7%8E%B0.png) 46 | 47 | 这里的输入图片例子是一个 $14\times 14\times 3$ 的图片,然后经过一个卷积核大小是 $5\times 5$ 的卷积层,输出是 $14\times 14\times 3$ ,接着是一个 Max pooling 层,参数是 $2\times 2$ ,输出就是 $5\times 5\times 16$ ,原本是接着两个 $400\times 400$ 的全连接层,现在改为用 $1\times 1\times 400$ 的两个卷积层。 48 | 49 | 接着是主要参考论文 **OverFeat** 来介绍如何通过卷积实现滑动窗口对象检测算法。 50 | 51 | 具体实现例子如下所示,第一行表示训练时候使用 $14\times 14\times 3$的图片,第二行表示测试时候使用的输入图片大小是 $16\times 16\times 3$。而使用这个图片,在经过卷积层的时候,这里步幅是 2,所以卷积核是移动了四次,得到了输出是 $12\times 12\times 16$,最终的输出也是 $2\times 2\times 4$。 52 | 53 | ![](https://cai-images-1257823952.cos.ap-beijing.myqcloud.com/%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E7%9A%84%E5%8D%B7%E7%A7%AF%E5%AE%9E2.png) 54 | 55 | 可以看到,其实在这 4 次卷积操作中有很多计算是重复的,因为有很多区域都是重叠的,具体四次如下所示,不同颜色的框表示四次操作的范围,左边第一个图的红色,然后移动 2 格,是第二个图中绿色框的区域,接着是第三张图里橙色,也就是左下角,然后第四张图里右下角,其实中间区域都是重叠的,也就是四个角落是有所不同。 56 | 57 | ![](https://cai-images-1257823952.cos.ap-beijing.myqcloud.com/%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E7%9A%84%E5%8D%B7%E7%A7%AF%E5%AE%9E%E7%8E%B0%203.png) 58 | 59 | 简单说,通过这个卷积操作,我们就可以不用将测试图片分割成 4 个子图片,分别输入网络中,执行前向操作,进行预测,直接整张图输入网络即可,卷积层就会帮我们完成这个操作,也就是一次前向操作即可,节省了 4 倍的时间。 60 | 61 | 62 | 63 | **不过,这种方法虽然提高了算法的效率,但也有一个缺点,就是边界框的位置可能不够准确。** 64 | 65 | 66 | 67 | ### Bounding Box预测(Bounding box predictions) 68 | 69 | 接下来要介绍如何可以得到精确的边界框,这里介绍的就是著名的 **YOLO(You only look once) 算法**,目前也是目标检测里很常用的一种算法,以及有了更多的版本,从最初的 YOLO,到目前的 YOLOv5,持续进行改进和提升。 70 | 71 | YOLO 算法的做法如下图所示,采用一个 $3\times 3$ 的网格,将输入图片分成了 9 个区域,然后检测每个区域内是否有目标对象,YOLO 算法会将检测到的对象,根据其中点位置,将其分配到中点所在的格子里,所以下图中编号 4 和 6 包含了汽车,但是编号 5 虽然同时有两辆车的一部分,但因为中心点不在,所以这个格子输出的结果是不包含有汽车。 72 | 73 | ![](https://cai-images-1257823952.cos.ap-beijing.myqcloud.com/YOLO%20%E7%AE%97%E6%B3%95.png ) 74 | 75 | 76 | 77 | 采用这个算法,网络输出的结果就是 $3\times 3 \times 8$ , 这里表示 $3\times 3$ 的网格,每个网格的结果是一个 8 维的向量,也是之前定义好的,即 $p_c, b_x, b_y, b_w, b_h, c_1, c_2, c_3$ 。 78 | 79 | **该算法的优点就是CNN 可以输、出精确的边界框**,在实践中可以采用更多的网格,比如 $19\times 19$,即便图片中包含多个对象,但如果网格数量越多,每个格子就越小,一个格子存在多个对象的概率就会很低。 80 | 81 | YOLO 算法的另一个优点是它采用卷积实现,速度非常快,这也是它很受欢迎的原因。 82 | 83 | ### 交并比(Intersection over union) 84 | 85 | 交并比(IoU)表示两个边界框交集和并集之比。并集就是如下图中绿色区域部分,即同时包含两个边界框的区域;而交集就是两个边界框重叠部分,下图中橙色区域。所以交并比就是橙色区域面积除以绿色区域的面积。 86 | 87 | ![](https://cai-images-1257823952.cos.ap-beijing.myqcloud.com/%E4%BA%A4%E5%B9%B6%E6%AF%94.png) 88 | 89 | 一般来说,IoU 大于等于 0.5,就可以说检测正确,结果是可以接受的,这也是一般的约定。但IoU 越大,边界框就约精确了。 90 | 91 | 这也是衡量定位精确到的一种方式,IoU 是衡量了两个边界框重叠的相对大小。 92 | 93 | 94 | 95 | ## 3. 非极大值抑制 96 | 97 | 目前的检测算法还会存在一个问题,就是对同一个对象作出多次的检测,而非极大值抑制就可以确保算法只对每个对象检测一次。 98 | 99 | 非极大值抑制算法的执行过程如下图所示,这里是采用 $19\times 19$ 的网格,对每个网格先执行检测算法,得到的输出就是 $19\times 19 \times 8$。当然这里只是预测是否有汽车,那么其实可以暂时不需要分类部分,也就是每个网格输出一个 5 维向量,$p_c$ 以及边界框的四个坐标参数。 100 | 101 | ![](https://cai-images-1257823952.cos.ap-beijing.myqcloud.com/%E9%9D%9E%E6%9E%81%E5%A4%A7%E5%80%BC%E6%8A%91%E5%88%B6.png) 102 | 103 | 104 | 105 | 然后开始实现非极大值抑制算法: 106 | 107 | 1. 去掉所有预测概率低于阈值的边界框,比如设置阈值是 0.6,那么对于 $p_c \le 0.6$ 的边界框都被抛弃; 108 | 2. 在剩下的边界框里,将预测概率最高的边界框,将其输出作为预测结果; 109 | 3. 然后将还剩下的边界框里,和第一步被抛弃的边界框有高 IoU 的,比如 $IoU \ge 0.5$ 的边界框都抛弃掉; 110 | 4. 对所有边界框都进行处理,按照上述 3 个步骤来判断,抛弃还是作为输出结果; 111 | 112 | 113 | 114 | ## 4. Anchor 115 | 116 | ### anchor boxes 介绍 117 | 118 | 上述说的检测都是限制于一个格子检测出一个对象,但如果需要一个格子可以检测多个对象,那么就需要用到 anchor box。 119 | 120 | 如下图所示,假设现在输入图片是左图中的例子,在第三行的第二个格子中是刚好同时存在人和汽车,并且中心点都落在这个格子里,但根据之前的算法,只能检测到其中一个对象。而要解决这个问题,就需要用到 anchor box 了。 121 | 122 | ![](https://cai-images-1257823952.cos.ap-beijing.myqcloud.com/Anchor_box.png ) 123 | 124 | **这里 anchor box 的思路是预先定义两个不同形状的 anchor box**,如上图的两个,当然实际情况里可能会采用更多的 anchor box,比如 5 个甚至更多。不过这里只需要两个即可。 125 | 126 | 接着就是重新定义标签,不再是开始的 8 维向量,而是 $2\times 8$ 的向量,**前面 8 个和 anchor box1 相关联,而后面 8 个和 anchor box2 相关联**。如上图右侧的 $y$ 所示。 127 | 128 | 在实际例子中,还有一些情况: 129 | 130 | - 比如使用两个 anchor box,但一个格子里出现 3 个对象,这种情况算法也处理不好; 131 | - 同个格子有两个对象,但它们的 anchor box 形状也一样,这种也是算法处理不好的情况; 132 | 133 | 另外,一般怎么选择 anchor box 呢?通常是**手工指定 anchor box 形状**,选择 5-10 个不同形状的,尽量覆盖多种不同的形状,覆盖你想要检测对象的各种形状。 134 | 135 | 另一种做法是在 YOLO 后期论文中介绍的,**k-平均算法**,用它来选择一组 anchor box,最具有代表性的一组 anchor box。 136 | 137 | 138 | 139 | ### Anchor 生成 140 | 141 | 参考文章: 142 | 143 | - [Faster RCNN之Anchors的生成过程理解](https://zhuanlan.zhihu.com/p/102978748) 144 | 145 | 146 | 147 | 参考的代码地址:https://github.com/kevinjliang/tf-Faster-RCNN 148 | 149 | 一般来说,anchor 的表现形式有两种: 150 | 151 | - 记录左上角和右下角的坐标 152 | - 记录中心点和宽高 153 | 154 | 所以需要注意注意标注的 anchor 形式,以及模型需要的 anchor 形式,相互之间做个转换还是比较简单的; 155 | 156 | 157 | 158 | 这份代码里生成 anchor 的函数是 `generator_anchors` ,具体代码地址:https://github.com/kevinjliang/tf-Faster-RCNN/blob/master/Lib/generate_anchors.py 159 | 160 | ```python 161 | def generate_anchors(base_size=16, ratios=[0.5, 1, 2], 162 | scales=2 ** np.arange(3, 6)): 163 | """ 164 | Generate anchor (reference) windows by enumerating aspect ratios X 165 | scales wrt a reference (0, 0, 15, 15) window. 166 | """ 167 | base_anchor = np.array([1, 1, base_size, base_size]) - 1 # [0,0,15,15] 168 | ratio_anchors = _ratio_enum(base_anchor, ratios) # shape: [3,4],返回的是不同长宽比的anchor 169 | anchors = np.vstack([_scale_enum(ratio_anchors[i, :], scales) 170 | for i in range(ratio_anchors.shape[0])]) # 生成九个候选框 shape: [9,4] 171 | return anchors 172 | ``` 173 | 174 | 这个函数有 3 个参数: 175 | 176 | 1. **base_size=16**:它指定的是类似感受野的区域大小,一般经过多层卷积和池化后得到的特征图,由于尺寸一般都会是原图的 $\frac{1}{2^n}$ ,其每个点对应原图都是一个区域,这里默认是 16 的话,表示对应原图里 $16\times 16$ 的区域大小; 177 | 2. **ratios=[0.5, 1, 2]**:这个参数是对基本的区域大小进行 3 种比例的变换,即 `1:2, 1:1, 2:1`,如下图所示,其实就是生成了 3 种不同形状的 anchor boxes 178 | 179 | 180 | 181 | 3. scales=2**np.arange(3, 6):这个作用就是对 anchor 进行指定倍数的增大,这里是以 2 为底数,然后是指数分别是 3,4,5,即分别放大了 8,16 和 32 倍,如下图所示: 182 | 183 | 184 | 185 | 首先对于第一个参数,定义一个基本的 anchor,这里代码是这样操作的: 186 | 187 | ```python 188 | base_anchor = np.array([1, 1, base_size, base_size]) - 1 189 | ``` 190 | 191 | 直接定义了一个左上角为 (0,0),右下角是(15,15)的 $16\times 16$ 的 anchor。 192 | 193 | 接下来是实现生成多个不同宽高比的 anchors,实现代码如下: 194 | 195 | ```python 196 | def _ratio_enum(anchor, ratios): # 这个函数计算不同长宽尺度下的anchor的坐标 197 | """ 198 | Enumerate a set of anchors for each aspect ratio wrt an anchor. 199 | """ 200 | w, h, x_ctr, y_ctr = _whctrs(anchor) # 找到anchor的中心点和长宽 201 | size = w * h # 返回anchor的面积 202 | size_ratios = size / ratios # 为了计算anchor的长宽尺度设置的数组:array([512.,256.,128.]) 203 | # 为什么要开根号来获得ws呢?我这里理解成通过面积来计算正方形的边长作为ws 204 | # 使ws:hs满足对应比例的同时,并且ws*hs的大小与base_anchor的大小相近的一种策略 205 | ws = np.round(np.sqrt(size_ratios)) # 计算不同长宽比下的anchor的宽:array([23.,16.,11.]) 206 | hs = np.round(ws * ratios) # 计算不同长宽比下的anchor的长 array([12.,16.,22.]) 207 | # 请大家注意,对应位置上ws和hs相乘,面积都为256左右 208 | anchors = _mkanchors(ws, hs, x_ctr, y_ctr) # 返回新的不同长宽比的anchor 返回的数组shape:[3,4],请注意anchor记录的是左上角和右下角的坐标 209 | return anchors 210 | ``` 211 | 212 | 这里生成不同宽高比的 anchor 方法,是通过这样的步骤: 213 | 214 | 1. 计算基础 anchor 的面积; 215 | 2. 根据给定的 ratios,计算不同宽高比下的面积 216 | 3. 对面积开方得到宽,然后乘以 ratio,得到长度 217 | 218 | 通过这种方式是即得到要求的宽高比,同时新的 anchor 的面积也和基础 anchor 的面积比较接近,比如上述得到新的anchor 的宽高分别是 `[23, 12], [16,16], [11,22]` ,其面积分别是 276,256 和 242,其中第二个就是基础 anchor 了; 219 | 220 | 221 | 222 | 这段代码中 `_whctrs` 是将 anchor 的表现形式转变为中心点+宽高的形式,实现如下: 223 | 224 | ```python 225 | def _whctrs(anchor): 226 | """ 227 | Return width, height, x center, and y center for an anchor (window). 228 | """ 229 | w = anchor[2] - anchor[0] + 1 230 | h = anchor[3] - anchor[1] + 1 231 | x_ctr = anchor[0] + 0.5 * (w - 1) 232 | y_ctr = anchor[1] + 0.5 * (h - 1) 233 | return w, h, x_ctr, y_ctr 234 | ``` 235 | 236 | 然后生成新的 anchor 的坐标的函数如下: 237 | 238 | ```python 239 | def _mkanchors(ws, hs, x_ctr, y_ctr): 240 | """ 241 | Given a vector of widths (ws) and heights (hs) around a center 242 | (x_ctr, y_ctr), output a set of anchors (windows). 243 | 给定一个宽和高的向量,以及中心点坐标,输出一组 anchors 244 | """ 245 | 246 | ws = ws[:, np.newaxis] 247 | hs = hs[:, np.newaxis] 248 | anchors = np.hstack((x_ctr - 0.5 * (ws - 1), 249 | y_ctr - 0.5 * (hs - 1), 250 | x_ctr + 0.5 * (ws - 1), 251 | y_ctr + 0.5 * (hs - 1))) 252 | return anchors 253 | ``` 254 | 255 | 所以这里将基础 anchor 进行ratio 的变换后得到新的 3 个 anchor 就是: 256 | 257 | ```python 258 | ratio_anchors = _ratio_enum(base_anchor, ratios) 259 | '''[[ -3.5, 2. , 18.5, 13. ], 260 | [ 0. , 0. , 15. , 15. ], 261 | [ 2.5, -3. , 12.5, 18. ]]''' 262 | ``` 263 | 264 | 接着最后一种变换就是进行 scale 的变换,即增大宽高,主要实现函数是 `_scale_enum` : 265 | 266 | ```python 267 | def _scale_enum(anchor, scales): 268 | """ 269 | Enumerate a set of anchors for each scale wrt an anchor. 270 | 对每一种长宽比的 anchor,计算不同面积尺度的 anchor 坐标 271 | """ 272 | w, h, x_ctr, y_ctr = _whctrs(anchor) 273 | ws = w * scales # shape [3,] 274 | hs = h * scales # shape [3,] 275 | anchors = _mkanchors(ws, hs, x_ctr, y_ctr) # 得到不同面积尺度的anchor信息,对应的是左上角和右下角的坐标 276 | return anchors 277 | ``` 278 | 279 | 这里对之前得到的 3 种不同宽高比的 anchor,分别进行 3 种 scale 的变换,所以最终得到的是 9 种 anchor boxes。 280 | 281 | 上述代码的实现过程也比较简单,首先将基础 anchor 变换成中心点+宽高的形式,然后根据给定的 scales 分别计算得到新的宽和高,接着就是生成对应的 anchor 的坐标。 282 | 283 | 经过这个变换得到的 9 种 anchor 坐标结果,这里是先按照 ratio 变换后的 anchor 的行维度进行遍历,因为 ratio_anchor 是 [3,4],所以会遍历 3 次,每次输入一种宽高比的 anchor 的坐标到 `_scale_enum` 函数中,在这个函数中刚刚也说了会计算 3 种 scale 的变换,得到的是 3 种不同宽高大小的 anchor,最终结果就是输出维度是 [9,4] 的 anchor boxes 了。 284 | 285 | ```python 286 | anchors = np.vstack([_scale_enum(ratio_anchors[i, :], scales) 287 | for i in range(ratio_anchors.shape[0])]) # 生成九个候选框 shape: [9,4] 288 | ''' 289 | [[ -84. -40. 99. 55.] 290 | [-176. -88. 191. 103.] 291 | [-360. -184. 375. 199.] 292 | [ -56. -56. 71. 71.] 293 | [-120. -120. 135. 135.] 294 | [-248. -248. 263. 263.] 295 | [ -36. -80. 51. 95.] 296 | [ -80. -168. 95. 183.] 297 | [-168. -344. 183. 359.]] 298 | ''' 299 | ``` 300 | 301 | 这里文章作者也做了一个表格,更清楚不同坐标的 ratio 和 scale : 302 | 303 | 304 | 305 | 另外这里得到的 anchor 坐标也说相对于原始图像大小的,而不是特征图上的大小,如下图所示: 306 | 307 | 308 | 309 | 根据上图,其实可以知道设计的这几种尺寸是基本可以包含图像中的任何物体了,当然除非这个物体非常大,那可能就需要调整 scale,使用更大的 scale 来包含特大的物体。 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | ------ 318 | 319 | ## 5. 候选区域 320 | 321 | 目标检测里另一个比较出名的算法,R-CNN,跟 YOLO 相比,是另一种思路,所以也基于此算法产生了很多检测算法,比如对其持续改进优化的,Fast-RCNN,Faster-RCNN 等。 322 | 323 | R-CNN 算法是尝试找到一些区域,在这部分区域里运行 CNN 进行检测。而选择这些候选区域的方法是运行图像分割算法,分割的结果如下图所示。根据分割算法得到的结果,在不同的区域运行分类器,判断该区域是否有目标对象,比如图中标注号码的几个区域。 324 | 325 | ![](https://cai-images-1257823952.cos.ap-beijing.myqcloud.com/%E5%80%99%E9%80%89%E5%8C%BA%E5%9F%9F.png) 326 | 327 | 328 | 这种做法相比滑动窗口,可能是不需要图片的每个地方都去检测一遍,只需要对分割算法得到的色块,加上边界框,然后对边界框内的区域运行分类器,虽然是工作量会减少一些,但实际上速度还是很慢,但优点是精度会很不错,这个就和 YOLO 算法刚好相反。 329 | 330 | 因为速度太慢的问题,后续也有很多改进的算法 331 | 332 | - Fast-RCNN:同样的方法进行候选区域的筛选,但通过卷积实现滑动窗口,对候选区域进行分类; 333 | - Faster-RCNN:采用 CNN 来生成候选区域 334 | 335 | 336 | 337 | ## 6. ROI Pooling 和 ROI Align 338 | 339 | 如果你对目标检测网络 Faster R-CNN 和实例分割网络 Mask R-CNN 网络比较熟悉的话,那你应该也对这个话题非常熟悉。 340 | 341 | 在区域建议网络 RPN 得到候选框 ROI 之后,需要提取该 ROI 中的固定数目的特征(例如Faster R-CNN中的 7*7 )输入到后面的分类网络以及边界回归网络的全连接层中。Faster R-CNN中使用的方法是 ROI Pooling,而对于像素位置精细度要求更高的 Mask R-CNN 对 ROI Pooling 进行改进,变成 ROI Align。 342 | 343 | **ROI Pooling和ROI Align最大的区别是:前者使用了两次量化操作,而后者并没有采用量化操作,使用了双线性插值算法,具体的解释如下所示。** 344 | 345 | 346 | 347 | ### ROI Pooling 技术细节 348 | 349 | img 350 | 351 | 如上图所示,为了得到固定大小(7X7)的feature map,我们需要做两次量化操作: 352 | 353 | 1. 图像坐标 — feature map坐标 354 | 2. feature map坐标 — ROI feature坐标。 355 | 356 | 我们来说一下具体的细节,如图我们输入的是一张800x800的图像,在图像中有两个目标(猫和狗),狗的BB大小为665x665,经过VGG16网络后,我们可以获得对应的feature map,如果我们对卷积层进行Padding操作,我们的图片经过卷积层后保持原来的大小,但是由于池化层的存在,我们最终获得feature map 会比原图缩小一定的比例,这和Pooling层的个数和大小有关。 357 | 358 | 在该VGG16中,我们使用了5个池化操作,每个池化操作都是2Pooling,因此我们最终获得feature map的大小为800/32 x 800/32 = 25x25(是整数),但是将狗的BB对应到feature map上面,我们得到的结果是665/32 x 665/32 = 20.78 x 20.78,结果是浮点数,含有小数,但是我们的像素值可没有小数,那么作者就对其进行了量化操作(即取整操作),即其结果变为20 x 20,在这里引入了第一次的量化误差; 359 | 360 | 然而我们的feature map中有不同大小的ROI,但是我们后面的网络却要求我们有固定的输入,因此,我们需要将不同大小的ROI转化为固定的ROI feature,在这里使用的是7x7的ROI feature,那么我们需要将20 x 20的ROI映射成7 x 7的ROI feature,其结果是 20 /7 x 20/7 = 2.86 x 2.86,同样是浮点数,含有小数点,我们采取同样的操作对其进行取整吧,在这里引入了第二次量化误差。 361 | 362 | 其实,**这里引入的误差会导致图像中的像素和特征中的像素的偏差**,即将feature空间的ROI对应到原图上面会出现很大的偏差。原因如下: 363 | 364 | 比如用我们第二次引入的误差来分析,本来是2.86,我们将其量化为2,这期间引入了0.86的误差,看起来是一个很小的误差呀,但是你要记得这是在feature空间,我们的feature空间和图像空间是有比例关系的,在这里是1:32,那么对应到原图上面的差距就是0.86 x 32 = 27.52。这个差距不小吧,这还是仅仅考虑了第二次的量化误差。**这会大大影响整个检测算法的性能**,因此是一个严重的问题。 365 | 366 | 367 | 368 | ### ROI Align 技术细节 369 | 370 | ![img](https://gitee.com/lcai013/image_cdn/raw/master/notes_images/roi_align.png) 371 | 372 | 如上图所示,为了得到为了得到固定大小(7X7)的feature map,**ROI Align技术并没有使用量化操作,即我们不想引入量化误差**,比如665 / 32 = 20.78,我们就用20.78,不用什么20来替代它,比如20.78 / 7 = 2.97,我们就用2.97,而不用2来代替它。这就是ROIAlign的初衷。 373 | 374 | 那么我们如何处理这些浮点数呢,我们的解决思路是使用“**双线性插值**”算法。双线性插值是一种比较好的图像缩放算法,它充分的利用了原图中虚拟点(比如20.56这个浮点数,像素位置都是整数值,没有浮点值)四周的四个真实存在的像素值来共同决定目标图中的一个像素值,即可以将 20.56 这个虚拟的位置点对应的像素值估计出来。 375 | 376 | 如下图所示,蓝色的虚线框表示卷积后获得的feature map,黑色实线框表示ROI feature,最后需要输出的大小是2x2,那么我们就利用双线性插值来估计这些蓝点(虚拟坐标点,又称双线性插值的网格点)处所对应的像素值,最后得到相应的输出。这些蓝点是2x2Cell中的随机采样的普通点,作者指出,**这些采样点的个数和位置不会对性能产生很大的影响**,你也可以用其它的方法获得。然后在每一个橘红色的区域里面进行max pooling或者average pooling操作,获得最终2x2的输出结果。 377 | 378 | 我们的整个过程中没有用到量化操作,没有引入误差,即原图中的像素和feature map中的像素是完全对齐的,没有偏差,**这不仅会提高检测的精度,同时也会有利于实例分割**。 379 | 380 | ![img](https://gitee.com/lcai013/image_cdn/raw/master/notes_images/roi_align2.png) 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | ------ 391 | 392 | # 参考 393 | 394 | 1. deeplearning.ai 04 课程第三周--目标检测 395 | 2. [Mask R-CNN详解](https://blog.csdn.net/WZZ18191171661/article/details/79453780) 396 | 3. [Faster RCNN之Anchors的生成过程理解](https://zhuanlan.zhihu.com/p/102978748) 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | -------------------------------------------------------------------------------- /Notes/经典的目标检测网络学习笔记.md: -------------------------------------------------------------------------------- 1 | # 经典的目标检测网络学习笔记 2 | 3 | 记录经典的一些目标检测网络模型,即 RCNN 系列、SSD、YOLO 系列的学习。 4 | 5 | 6 | 7 | ------ 8 | 9 | ## 1. SSD 10 | 11 | 参考文章: 12 | 13 | - [目标检测|SSD原理与实现](https://zhuanlan.zhihu.com/p/33544892) 14 | 15 | 16 | 17 | SSD,英文全名是 Single Shot MultiBox Detector,论文地址: 18 | 19 | [SSD: Single Shot MultiBox Detector](https://arxiv.org/pdf/1512.02325v5.pdf) 20 | 21 | 代码地址: 22 | 23 | 1. 官方地址:https://github.com/weiliu89/caffe 24 | 2. pytorch:https://github.com/amdegroot/ssd.pytorch 25 | 3. tensorflow:https://github.com/balancap/SSD-Tensorflow 26 | 4. mmdetection:https://github.com/open-mmlab/mmdetection 27 | 28 | 29 | 30 | SSD 算法属于 one-stage 的方法,并且是多框检测。下面给出了几种不同检测算法的基本框架图,图片来自参考文章 1: 31 | 32 | 33 | 34 | 根据上图,MultiBox 和 Faster-RCNN 都是 two-stage 的算法,而 YOLO 和 SSD 都是 one-stage 的算法,这两类算法的不同之处在于: 35 | 36 | 1. two-stage 一般是先通过一些启发式方法(比如 selective search)或者 CNN 网络(RPN)产生一些稀疏的候选框(这阶段就过滤了大量非目标物理的检测框),然后再对这些候选框进行分类和回归,其优势是**准确率高,但速度较慢**; 37 | 2. one-stage 方法的主要思路就是在图片不同位置均匀的进行密集抽样,主要是通过 anchor box 的方法,设置了不同尺度和长宽比的 anchors,然后利用 CNN 进行提取特征并进行分类和回归,直接一步完成,**优势就是速度快,但是缺点是训练很困难**,因为候选框过多,并且大量是负样本,即**正负样本极其不均衡**,导致模型准确率相比 two-stage 算法是稍差的; 38 | 39 | 40 | 41 | 而 SSD 和 YOLO 的区别在于: 42 | 43 | 1. Yolo 是在全连接层后做检测,而 SSD 则是采用卷积直接做检测; 44 | 2. SSD 采用不同尺度的特征图来做检测,大尺度的特征图(比较浅层的特征图)用来检测小物体,而小尺度特征图(深层的特征图)来检测大物体; 45 | 3. SSD 设置了不同的尺度和长宽比的先验框,Prior boxes,在 Faster-RCNN 中是叫做 Anchors 46 | 47 | YOLO 存在的问题是难以检测小目标,并且定位不够准确,而 SSD 上述的改进则是为了避免 YOLO 的这些问题。 48 | 49 | 50 | 51 | ### 设计理念 52 | 53 | SSD 的基本结构如下: 54 | 55 | 56 | 57 | SSD 的核心设计理念主要是以下 3 点: 58 | 59 | 1. 采用多尺度的特征图 60 | 2. 使用卷积进行检测 61 | 3. 设置先验框 62 | 63 | 64 | 65 | #### 采用多尺度特征图 66 | 67 | 如下图所示,分别采用了 $8\times 8$ 和 $4\times 4$ 两种特征图,使用相同大小的检测框(也是$3\times 3$ 左右的大小),可以看到在大的特征图上可以检测到较小的目标,而小的特征图上可以检测大目标,这就是使用多尺度特征图的原因,不同尺度的特征图负责检测不同大小的目标,这也是解决了 yolo 不能检测到小目标的缺陷。 68 | 69 | 70 | 71 | 72 | 73 | #### 使用卷积进行检测 74 | 75 | SSD 在最后直接采用卷积层来对不同的特征图提取检测结果,对于大小是 $m\times n\times p$ 的特征图,只需要采用 $3\times 3 \times p$ 这样比较小的卷积核就可以得到检测值,相比最后采用全连接层的 Yolo 可以进一步减少参数。 76 | 77 | 78 | 79 | #### 设置先验框 80 | 81 | 在 Yolo 中每个单元预测多个边界框,但都是相对这个单元本身(正方块),但真实目标可能是多种不同形状,这需要 Yolo 在训练中自适应目标的形状。 82 | 83 | 而 SSD 则借鉴了 Faster R-CNN 中 anchor 的理念,也就是每个单元设置多个尺度或者长宽比不同的先验框(Prior boxes),预测框以这些先验框为基准,这一定程度上减少了训练难度。如下图所示,一般每个单元会设置多个先验框,它们在尺度和长宽比上都存在差异,比如下图就是每个单元使用了 4 个不同的先验框,并且长宽比也不一样,有正方形,也有更宽的长方形或者更长的长方形。 84 | 85 | 86 | 87 | SSD 输出的检测值是包含两部分: 88 | 89 | 1. **每个类别的置信度或者评分**:SSD 是将背景也当做一个特殊的类别,所以假如检测的目标类别有 c 个,那么SSD 会输出 c+1 个置信度,第一个置信度就是指背景的置信度,也是不包含任何目标,然后将检测框的类别设置为置信度最高的那个类别; 90 | 2. **位置 location**:包含 4 个值,分别是 (cx, cy, w, h),分别表示中心点坐标和宽高。 91 | 92 | 不过,注意这里预测框的location 其实是相对于先验框的转换值(论文里说是 offset)。假设先验框位置为 $d=(d_{cx},d_{cy},d_w,d_h)$ ,则其对应的标注框 $b=(b_{cx},b_{cy},b_w,b_h)$,则其预测框 l 其实是 b 相对于 d 的转换值,如下所示: 93 | $$ 94 | l_{cx} = \frac{b_{cx}-d_{cx}}{d_w},l_{cy}=\frac{b_{cy}-d_{cy}}{d_h}\\ 95 | l_w=log(\frac{b_w}{d_w}),l_h=log(\frac{b_h}{d_h}) 96 | $$ 97 | 习惯上来说,上述过程称为对标注框的编码(encode),而预测的时候,需要反过来,即进行解码(decode),从预测框 l 得到真实的标注框 b: 98 | $$ 99 | b_{cx} = d_wl_{cx}+d_{cx},b_{cy}=d_hl_{cy}+d_{cy}\\ 100 | b_w=d_w exp(l_w),b_h=d_h exp(l_h) 101 | $$ 102 | 不过在 [caffe 版本的 SSD 代码 ](https://github.com/weiliu89/caffe/tree/ssd)实现中有个 trick,可以设置一个 `variance` 超参数来调整预测值,通过布尔型参数 `variance_encoded_in_target` 来控制两种模式,当它为 True 的时候,表示 `variance` 被包含在预测值里,即上述的情况;而如果是 False(大部分是这种方式,可能更易于训练),就需要手动设置这个超参数 `variance` ,用来对预测框 l 的四个值进行缩放,即如下进行解码: 103 | $$ 104 | b_{cx} = d_w(variance[0] * l_{cx})+d_{cx},b_{cy}=d_h(variance[1] * l_{cy})+d_{cy}\\ 105 | b_w=d_w exp(variance[2] * l_w),b_h=d_h exp(variance[3] * l_h) 106 | $$ 107 | 综上所述,对于一个大小 $m\times n$ 的特征图,总共有 $mn$ 个单元(即每个像素点都是一个单元),每个单元设置的先验框数量记为 k,则每个单元需要 $(c+4)k$ 个预测值(每个预测框是 c 个置信度+4 个location 数值),所有单元就需要 $(c+4) kmn$ 个预测值,而因为 SSD 采用卷积做检测,所以需要 $(c+4)k$ 个卷积核完成这个特征图的检测过程。 108 | 109 | 110 | 111 | ### 网络结构 112 | 113 | SSD 是采用 VGG16 作为 backbone,并在此基础增加更多的卷积层来完成检测,如下图所示,上面是 SSD 的网络结构,而下面是 Yolo 的网络结构。模型输入图片大小是 $300\times 300$ ,当然也可以是 $512\times 512$ ,只需要最后新增一个卷积层。 114 | 115 | 116 | 117 | SSD 将 VGG16 原本的全连接层 fc6 和 fc7 转换为 $3\times3 $ 的卷积层 conv6 和 $1\times 1$ 的卷积层 conv7,并将池化层 p5 从原来是 stride=2 的$2\times 2$ 改为 stride=1 的 $3\times 3$ (这里应该是不改变特征图大小),并且为了配合这种变化,采用了一种 Atrous Algothrim,操作上就是 conv6 使用了扩展卷积或者带孔卷积(Dilation Conv),它可以在不增加参数和模型复杂度的前提下指数级扩大卷积的视野,通过参数扩张率 dilation rate 来表示扩张的大小,如下图所示,a 图是普通的卷积核,其视野只是 $3\times 3$ ,而 b 图的扩展率是 2,视野就是 $7\times 7$ ,c 图的扩张率是 4,视野就变成了 $15\times 15$ ,但是缺点是特征会变得稀疏。这里 conv6 层采用了 $3\times 3$ 的卷积核大小,扩张率是 6 的扩展视野。 118 | 119 | 120 | 121 | 然后还移除了 dropout 层和 fc8 层,并新增更多的卷积层。 122 | 123 | 124 | 125 | 这里 VGG16 的 Conv4_3 层将作为检测的第一个特征图,其特征图大小是 $38\times 38$,因为这层太靠前,其 norm 比较大,所以之后接了一个 L2 Normalizaton 层(参考 [ParseNet](https://arxiv.org/abs/1506.04579)),以保证和后面的检测层差异不会太大,该层的作用是对每个像素点在 channel 维度上做归一化,和 BN 不太一样,BN 是在 [batch, width, height] 三个维度上做归一化。 126 | 127 | 归一化后还会设置一个可训练的缩放变量 gamma,在 Tensorflow 中的实现如下: 128 | 129 | ```python 130 | # l2norm (not bacth norm, spatial normalization) 131 | def l2norm(x, scale, trainable=True, scope="L2Normalization"): 132 | n_channels = x.get_shape().as_list()[-1] 133 | l2_norm = tf.nn.l2_normalize(x, [3], epsilon=1e-12) 134 | with tf.variable_scope(scope): 135 | gamma = tf.get_variable("gamma", shape=[n_channels, ], dtype=tf.float32, 136 | initializer=tf.constant_initializer(scale), 137 | trainable=trainable) 138 | return l2_norm * gamma 139 | ``` 140 | 141 | SSD 还将提取 Conv7,Conv8_2,Conv9_2,Conv10_2,Conv11_2 作为检测所用的特征图,加上Conv4_3层,共提取了6个特征图,其大小分别是 $(38,38), (19,19),(10,10),(5,5),(3,3),(1,1)$ ,**但不同特征图会设置不同的先验框数量**,当然同个特征图的每个单元是相同数量的先验框。 142 | 143 | 先验框的设置包括两个方面,尺度和长宽比。首先,对于先验框的尺度,遵守一个线性递增规则:**随着特征图大小降低,先验框尺度线性增加**,如下所示: 144 | $$ 145 | s_k = s_{min}+\frac{s_{max}-s_{min}}{m-1}(k-1),k\in[1,m] 146 | $$ 147 | 这里 m 表示特征图的个数,在 SSD 里是设置为 5,因为第一层(Conv4_3) 是单独设置,而 $s_k$ 表示先验框大小相对于原图的比例,$s_{min},s_{max}$ 分别表示比例的最小值和最大值,论文里是分别设置 0.2 和 0.9。 148 | 149 | 对于第一个特征图(Conv4_3层的特征图),一般设置先验框的尺度比例为 $\frac{s_{min}}{2}= 0.1$ ,所以尺度就是原图大小 300 * 0.1 = 30。 150 | 151 | 对于后面的特征图,则按照上述公式线性增加,其操作步骤如下: 152 | 153 | 1. 首先一般会将尺度比例先扩大 100 倍,即 $\lfloor \frac{\lfloor s_{max}\times 100\rfloor - \lfloor s_{min}\times 100\rfloor}{m-1}\rfloor=17$,这样 5 个特征图的 $s_k$ 分别是 20,37,54,71 和 88 154 | 2. 再将它们除以 100 并乘以图片大小,得到的每个特征图的尺度就是 60,111,162,213,264,加上刚刚单独算的第一个特征图的尺度,**结果就是 30,60,111,162,213,264** 155 | 156 | 157 | 158 | 对于长宽比,一般选择 $a_r\in \{1,2,3,\frac{1}{2},\frac{1}{3}\}$ ,对于特定的长宽比,按如下公式计算先验框的宽度和高度: 159 | $$ 160 | w_k^a = p_k\sqrt{a_r}, h_k^a = \frac{p_k}{\sqrt{a_r}} 161 | $$ 162 | 其中 $p_k$ 表示先验框实际尺度,默认情况下每个特征图都有一个 $a_r=1$ 且尺度是 $p_k$ 的先验框,除此之外还会设置一个尺度为 $p'_{k}=\sqrt{p_k p_{k+1}}$ 且 $a_r=1$ 的先验框,即每个特征图就设置了两个长宽比都是 1,但是尺度不同的正方形先验框。 163 | 164 | 注意,最后一个特征图是要参考一个虚拟的$p_{m+1}=300\times 105 /100 = 315$ 来计算 $p'_{m}$ 。 165 | 166 | 因此,每个特征图一共有 6 个先验框 $\{1,2,3,\frac{1}{2},\frac{1}{3}, 1'\}$,不过注意,实际中,Conv4_3, Conv10_2 和 Conv11_2 仅使用 4 个先验框,它们不使用长宽比是 3 和 $\frac{1}{3}$ 的先验框。 167 | 168 | 每个单元先验框的中心点分布在各个单元的中心,即 $(\frac{i+0.5}{|f_k|},\frac{j+0.5}{|f_k|}),i,j\in[0, |f_k|)$,其中 $|f_k|$ 表示特征图的大小。 169 | 170 | 上述是给出了先验框的设置,而下图给出了一个 $5\times 5$ 大小的特征图的检测过程,主要分为三个部分: 171 | 172 | 1. 生成先验框 173 | 2. 得到类别置信度 174 | 3. 得到边界框位置 175 | 176 | 其中第一步是上述介绍的,后面两步其实都是通过 $3\times 3$ 的卷积来完成,假设 $n_k$ 是该特征图采用的先验框数量,那么类别置信度需要的卷积核数量就是 $n_k \times c$ ,边界框位置需要的卷积核数量是 $n_k \times 4$ ,因为每个先验框都会预测一个边界框,因此 SSD300 一共可以预测 $38\times38\times4+19\times19\times6+10\times10\times6+5\times5\times6+3\times3\times4+1\times1\times4=8732$ 个边界框,**所以 SSD 本质上是密集采样**,或者说对于 one-stage 的检测算法,都属于密集采样。 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | ### 训练过程 185 | 186 | #### 先验框匹配 187 | 188 | 训练过程首先是需要确定图片中的 ground truth(即标注框)和哪个先验框进行匹配,匹配的先验框对应的预测框将负责预测这个标注框。 189 | 190 | **在 Yolo 中的规则是标注框的中心落在哪个单元,则该单元和其 IOU 最大的边界框负责预测它**。 191 | 192 | 但 SSD 采用不同的匹配原则,主要是两点: 193 | 194 | 1. 对每个标注框都找到和其 IOU 最大的先验框,该先验框和它匹配,保证每个标注框都有一个先验框匹配和对其进行预测,这种先验框也称为正样本,而如果没有匹配上标注框的先验框则是负样本。 195 | 2. 对于没有满足第一个原则的其他未匹配先验框,如果和某个标注框的 IOU 大于某个阈值(通常是 0.5),则该先验框也匹配该标注框,这表示一个标注框可以和多个先验框进行匹配,但是反过来是不可以的,不能一个先验框匹配多个标注框。 196 | 197 | 198 | 199 | 第一个原则会导致的一个问题就是负样本太多,因为标注框通常很少,而先验框非常多,这导致正负样本极其不平衡,所以也就有了第二个原则。 200 | 201 | 第二个原则在代码实现里是有多种实现方式的,比如: 202 | 203 | 1. [Tensorflow 版本](https://github.com/xiaohu2015/SSD-Tensorflow/blob/master/nets/ssd_common.py) 是只实施了第二个原则; 204 | 2. [Pytorch 版本 ](https://github.com/amdegroot/ssd.pytorch/blob/master/layers/box_utils.py)实施了两个原则 205 | 206 | 如下图示一个匹配示意图,绿色的GT 表示 ground truth,红色框是先验框,FP 表示负样本,TP 表示正样本。 207 | 208 | ![](https://github.com/ccc013/DeepLearning_Notes/blob/master/images/SSD_8.png) 209 | 210 | 尽管有上述两个原则,让一个标注框可以匹配多个先验框,但是由于先验框和标注框的数量差距还是很大,负样本还是会远多于正样本,所以 SSD 还采用了常见的一种处理样本不平衡的方法-- hard negative mining,即对负样本进行抽样,抽样是按照置信度误差(预测背景的置信度越小,误差越大)进行降序排列,然后选择误差较大的 top-k 作为训练时候的负样本,以保证正负样本比接近 1:3。 211 | 212 | 213 | 214 | #### 损失函数 215 | 216 | SSD 的损失函数是两部分组成,位置误差(locatization loss,los)和置信度误差(confidence loss,conf)的加权和,如下所示: 217 | $$ 218 | L(x, c, l, g) = \frac{1}{N}(L_{conf}(x,c) + \alpha L_{loc}(x,l,g)) 219 | $$ 220 | N 表示先验框的正样本数量,$x^p_{ij}\in \{1,0\}$ 作为一个指示参数,当它为 1 的时候表示第 i 个先验框和第 j 个标注框匹配,并且标注框的类别是 p 。 221 | 222 | c 为类别置信度预测值。l 是先验框对应的边界框的位置预测值,g 是标注框的位置参数,对于位置误差,采用的是 Smooth L1 Loss,定义如下: 223 | 224 | ![](https://github.com/ccc013/DeepLearning_Notes/blob/master/images/SSD_9.png) 225 | 226 | 因为 $x^p_{ij}$ 的原因,所以位置误差仅针对正样本进行计算。需要注意的是,先要对标注框的 g 进行编码得到 $\hat{g}$ ,因为预测值 l 也是编码值,如果设置了 `variance_encoded_in_target=True` ,编码的时候就要加上 `variance` : 227 | $$ 228 | \hat{g}^{cx}_j = (g^{cx}_j - d^{cx}_i)/d^w_i/variance[0], \hat{g}^{cy}_j = (g^{cy}_j - d^{cy}_i)/d^h_i/variance[1]\\ 229 | \hat{g}^{w}_j = \log(g^{w}_j/d^w_i)/variance[2], \space \hat{g}^{h}_j = \log(g^{h}_j/d^h_i)/variance[3] 230 | $$ 231 | 对于位置误差,采用的是 softmax loss: 232 | 233 | 234 | 235 | 权重参数 $\alpha$ 通过交叉验证设置为 1 236 | 237 | 238 | 239 | #### 数据扩增 240 | 241 | 采用的数据扩增方法主要有水平翻转(horizontal flip)、随机裁剪和颜色扭曲(random crop & color distortion),随机采集块(Randomly sample a patch)(获取小目标训练样本)。 242 | 243 | 244 | 245 | ### 预测过程 246 | 247 | 预测过程比较简单,对于每个预测框,首先根据类别置信度确定其类别(置信度最大者)与置信度值,并过滤掉属于背景的预测框。然后根据置信度阈值(如0.5)过滤掉阈值较低的预测框。对于留下的预测框进行解码,根据先验框得到其真实的位置参数(解码后一般还需要做clip,防止预测框位置超出图片)。解码之后,一般需要根据置信度进行降序排列,然后仅保留top-k(如400)个预测框。最后就是进行NMS算法,过滤掉那些重叠度较大的预测框。最后剩余的预测框就是检测结果了。 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | -------------------------------------------------------------------------------- /Practise/Apriori.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | @Time : 2019/4/5 15:38 5 | @Author : luocai 6 | @file : Apriori.py 7 | @concat : 429546420@qq.com 8 | @site : 9 | @software: PyCharm Community Edition 10 | @desc : 11 | 12 | """ 13 | 14 | 15 | def loadDataSet(): 16 | # a simple test dataset 17 | return [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]] 18 | 19 | 20 | # 创建一个大小为 1 的所有候选项集的集合 21 | def createC1(dataset): 22 | C1 = [] 23 | for transaction in dataset: 24 | for item in transaction: 25 | if not [item] in C1: 26 | C1.append([item]) 27 | # 排序 28 | C1.sort() 29 | # python3 使用 map 直接返回的是一个对象 30 | return list(map(frozenset, C1)) 31 | 32 | 33 | # 计算所有项集的支持度 34 | def scanD(D, Ck, minSupport): 35 | ''' 36 | 对输入的候选项集 Ck,计算每个项集的支持度,并返回符合要求的项集和所有频繁项集 37 | :param D: 数据集 38 | :param Ck: 包括 k 个物品的候选项集 39 | :param minSupport: 最小支持度 40 | :return: 41 | retList: 满足最小支持度的候选项集 42 | supportData: 所有的频繁项集和其支持度的数据 43 | ''' 44 | # ssCnt 是存储每个候选项集的支持度的字典 45 | ssCnt = {} 46 | for tid in D: 47 | for can in Ck: 48 | if can.issubset(tid): 49 | ssCnt.setdefault(can, 1) 50 | ssCnt[can] += 1 51 | 52 | numItems = float(len(D)) 53 | retList = [] 54 | supportData = {} 55 | # 计算支持度 56 | for key in ssCnt: 57 | support = ssCnt[key] / numItems 58 | if support >= minSupport: 59 | retList.insert(0, key) 60 | supportData[key] = support 61 | 62 | return retList, supportData 63 | 64 | 65 | def aprioriGen(Lk, k): 66 | ''' 67 | 生成 Ck 候选项集 68 | :param Lk: 频繁项集列表 69 | :param k: 项集元素个数 70 | :return: 返回指定物品数量的候选项集 71 | ''' 72 | retList = [] 73 | lenLk = len(Lk) 74 | for i in range(lenLk): 75 | for j in range(i + 1, lenLk): 76 | # 获取两个相邻项集的前 k-2 项 77 | L1 = list(Lk[i])[:k - 2] 78 | L2 = list(Lk[j])[:k - 2] 79 | L1.sort() 80 | L2.sort() 81 | if L1 == L2: 82 | # 如果两个集合相同,进行合并 83 | retList.append(Lk[i] | Lk[j]) 84 | return retList 85 | 86 | 87 | def apriori(dataSet, minSupport=0.5): 88 | ''' 89 | Apriori 算法入口 90 | :param dataSet: 数据集 91 | :param minSupport: 最小支持度 92 | :return: 93 | L: 最终的满足最小支持度的候选项集 94 | supportData: 所有的频繁项集和其支持度的数据 95 | ''' 96 | C1 = createC1(dataSet) 97 | # 创建集合表示的数据集 98 | D = list(map(set, dataSet)) 99 | L1, supportData = scanD(D, C1, minSupport) 100 | L = [L1] 101 | k = 2 102 | while len(L[k - 2]) > 0: 103 | Ck = aprioriGen(L[k - 2], k) 104 | Lk, supK = scanD(D, Ck, minSupport) 105 | supportData.update(supK) 106 | L.append(Lk) 107 | # 增加候选项集的物品数量 108 | k += 1 109 | return L, supportData 110 | 111 | def rulesFromConseq(freqSet, H, supportData, br1, minConf=0.7): 112 | ''' 113 | 生成候选规则集合 114 | :param freqSet: 频繁项集 115 | :param H: 规则右部的元素列表 116 | :param supportData: 117 | :param br1: 118 | :param minConf: 可信度阈值 119 | :return: 120 | ''' 121 | m = len(H[0]) 122 | 123 | 124 | 125 | 126 | if __name__ == '__main__': 127 | dataSet = loadDataSet() 128 | print('test dataset:', dataSet) 129 | # C1 = createC1(dataSet) 130 | # print('C1:', C1) 131 | # # 构建集合表示的数据集 D 132 | # D = list(map(set, dataSet)) 133 | # print('D:', D) 134 | # L1, suppData0 = scanD(D, C1, minSupport=0.5) 135 | # print('L1:', L1) 136 | 137 | L, suppData = apriori(dataSet) 138 | for i, val in enumerate(L): 139 | print('{}: {}'.format(i, val)) 140 | for key, data in suppData.items(): 141 | print('{}: {}'.format(key, data)) 142 | -------------------------------------------------------------------------------- /Practise/Loss_functions_practise.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | @Time : 2019/1/23 23:09 5 | @Author : cai 6 | 7 | practise for different loss function 8 | """ 9 | import numpy as np 10 | 11 | 12 | def rmse(predictions, targets): 13 | # 真实值和预测值的误差 14 | differences = predictions - targets 15 | differences_squared = differences ** 2 16 | mean_of_differences_squared = differences_squared.mean() 17 | # 取平方根 18 | rmse_val = np.sqrt(mean_of_differences_squared) 19 | return rmse_val 20 | 21 | 22 | def mae(predictions, targets): 23 | differences = predictions - targets 24 | absolute_differences = np.absolute(differences) 25 | mean_absolute_differences = absolute_differences.mean() 26 | return mean_absolute_differences 27 | 28 | 29 | def mbe(predictions, targets): 30 | differences = predictions - targets 31 | mean_absolute_differences = differences.mean() 32 | return mean_absolute_differences 33 | 34 | 35 | def hinge_loss(predictions, label): 36 | ''' 37 | hinge_loss = max(0, s_j - s_yi +1) 38 | :param predictions: 39 | :param label: 40 | :return: 41 | ''' 42 | result = 0.0 43 | pred_value = predictions[label] 44 | print('pred_value={}'.format(pred_value)) 45 | for i, val in enumerate(predictions): 46 | if i == label: 47 | continue 48 | tmp = val - pred_value + 1 49 | result += max(0, tmp) 50 | return result 51 | 52 | 53 | def cross_entropy(predictions, targets, epsilon=1e-10): 54 | predictions = np.clip(predictions, epsilon, 1. - epsilon) 55 | N = predictions.shape[0] 56 | ce_loss = -np.sum(np.sum(targets * np.log(predictions + 1e-5))) / N 57 | return ce_loss 58 | 59 | 60 | def loss_test(): 61 | y_hat = np.array([0.000, 0.166, 0.333]) 62 | y_true = np.array([0.000, 0.254, 0.998]) 63 | 64 | print("d is: " + str(["%.8f" % elem for elem in y_hat])) 65 | print("p is: " + str(["%.8f" % elem for elem in y_true])) 66 | rmse_val = rmse(y_hat, y_true) 67 | print("rms error is: " + str(rmse_val)) 68 | mae_val = mae(y_hat, y_true) 69 | print("mae error is: " + str(mae_val)) 70 | 71 | mbe_val = mbe(y_hat, y_true) 72 | print("mbe error is: " + str(mbe_val)) 73 | 74 | image1 = np.array([-0.39, 1.49, 4.21]) 75 | image2 = np.array([-4.61, 3.28, 1.46]) 76 | image3 = np.array([1.03, -2.37, -2.27]) 77 | result1 = hinge_loss(image1, 0) 78 | result2 = hinge_loss(image2, 1) 79 | result3 = hinge_loss(image3, 2) 80 | print('image1,hinge loss={}'.format(result1)) 81 | print('image2,hinge loss={}'.format(result2)) 82 | print('image3,hinge loss={}'.format(result3)) 83 | 84 | predictions = np.array([[0.25, 0.25, 0.25, 0.25], 85 | [0.01, 0.01, 0.01, 0.96]]) 86 | targets = np.array([[0, 0, 0, 1], 87 | [0, 0, 0, 1]]) 88 | cross_entropy_loss = cross_entropy(predictions, targets) 89 | print("Cross entropy loss is: " + str(cross_entropy_loss)) 90 | 91 | 92 | if __name__ == '__main__': 93 | loss_test() 94 | print('finish!') 95 | 96 | -------------------------------------------------------------------------------- /Practise/Perfermance_measures.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | @Time : 2019/3/14 23:12 5 | @Author : cai 6 | 7 | 模型的性能度量,二分类 8 | 模型评估方法,划分数据集 9 | """ 10 | from sklearn.model_selection import train_test_split 11 | from sklearn.datasets import load_iris 12 | from sklearn.neighbors import KNeighborsClassifier 13 | from sklearn.cross_validation import cross_val_score 14 | from sklearn.model_selection import StratifiedKFold 15 | from sklearn.base import clone 16 | 17 | 18 | # ======================================= 19 | # 性能度量方法 20 | # ======================================= 21 | 22 | def accuracy(y_true, y_pred): 23 | return sum(y == y_p for y, y_p in zip(y_true, y_pred)) / len(y_true) 24 | 25 | 26 | def error(y_true, y_pred): 27 | return sum(y != y_p for y, y_p in zip(y_true, y_pred)) / len(y_true) 28 | 29 | 30 | def precision(y_true, y_pred): 31 | true_positive = sum(y and y_p for y, y_p in zip(y_true, y_pred)) 32 | predicted_positive = sum(y_pred) 33 | return true_positive / predicted_positive 34 | 35 | 36 | def recall(y_true, y_pred): 37 | true_positive = sum(y and y_p for y, y_p in zip(y_true, y_pred)) 38 | real_positive = sum(y_true) 39 | return true_positive / real_positive 40 | 41 | 42 | def true_negative_rate(y_true, y_pred): 43 | true_negative = sum(1 - (yi or yi_hat) for yi, yi_hat in zip(y_true, y_pred)) 44 | actual_negative = len(y_true) - sum(y_true) 45 | return true_negative / actual_negative 46 | 47 | 48 | def roc(y, y_hat_prob): 49 | thresholds = sorted(set(y_hat_prob), reverse=True) 50 | ret = [[0, 0]] 51 | for threshold in thresholds: 52 | y_hat = [int(yi_hat_prob >= threshold) for yi_hat_prob in y_hat_prob] 53 | ret.append([recall(y, y_hat), 1 - true_negative_rate(y, y_hat)]) 54 | return ret 55 | 56 | 57 | def get_auc(y, y_hat_prob): 58 | roc_val = iter(roc(y, y_hat_prob)) 59 | tpr_pre, fpr_pre = next(roc_val) 60 | auc = 0 61 | for tpr, fpr in roc_val: 62 | auc += (tpr + tpr_pre) * (fpr - fpr_pre) / 2 63 | tpr_pre = tpr 64 | fpr_pre = fpr 65 | return auc 66 | 67 | 68 | # ======================================= 69 | # 模型评估方法 70 | # ======================================= 71 | 72 | def hold_out(): 73 | # 加载 Iris 数据集 74 | dataset = load_iris() 75 | # 划分训练集和测试集 76 | (trainX, testX, trainY, testY) = train_test_split(dataset.data, dataset.target, random_state=3, test_size=0.3) 77 | # 建立模型 78 | knn = KNeighborsClassifier() 79 | # 训练模型 80 | knn.fit(trainX, trainY) 81 | # 将准确率打印出 82 | print('hold_out, score:', knn.score(testX, testY)) 83 | 84 | 85 | def cross_validation(cv=10): 86 | # 加载 Iris 数据集 87 | dataset = load_iris() 88 | data = dataset.data 89 | label = dataset.target 90 | # 建立模型 91 | knn = KNeighborsClassifier() 92 | # 使用K折交叉验证模块 93 | scores = cross_val_score(knn, data, label, cv=cv, scoring='accuracy') 94 | print('cross_validation numbers=', cv) 95 | # 将每次的预测准确率打印出 96 | print(scores) 97 | # 将预测准确平均率打印出 98 | print(scores.mean()) 99 | 100 | 101 | def StratifiedKFold_method(n_splits=3): 102 | ''' 103 | 分层采样 104 | :return: 105 | ''' 106 | # 加载 Iris 数据集 107 | dataset = load_iris() 108 | data = dataset.data 109 | label = dataset.target 110 | # 建立模型 111 | knn = KNeighborsClassifier() 112 | print('use StratifiedKFold') 113 | skfolds = StratifiedKFold(n_splits=n_splits, random_state=42) 114 | scores = 0. 115 | for train_index, test_index in skfolds.split(data, label): 116 | clone_clf = clone(knn) 117 | X_train_folds = data[train_index] 118 | y_train_folds = (label[train_index]) 119 | X_test_fold = data[test_index] 120 | y_test_fold = (label[test_index]) 121 | clone_clf.fit(X_train_folds, y_train_folds) 122 | y_pred = clone_clf.predict(X_test_fold) 123 | n_correct = sum(y_pred == y_test_fold) 124 | print(n_correct / len(y_pred)) 125 | scores += n_correct / len(y_pred) 126 | print('mean scores:', scores / n_splits) 127 | 128 | 129 | if __name__ == '__main__': 130 | y_true = [1, 0, 1, 0, 1] 131 | y_pred = [0, 0, 1, 1, 0] 132 | y_hat_prob = [0.9, 0.85, 0.8, 0.7, 0.6] 133 | 134 | acc = accuracy(y_true, y_pred) 135 | err = error(y_true, y_pred) 136 | precisions = precision(y_true, y_pred) 137 | recalls = recall(y_true, y_pred) 138 | print('accuracy=', acc) 139 | print('error=', err) 140 | print('precisions=', precisions) 141 | print('recalls=', recalls) 142 | 143 | roc_list = roc(y_true, y_hat_prob) 144 | auc_val = get_auc(y_true, y_hat_prob) 145 | print('roc_list:', roc_list) 146 | print('auc_val:', auc_val) 147 | 148 | hold_out() 149 | cross_validation() 150 | StratifiedKFold_method() 151 | -------------------------------------------------------------------------------- /Pytorch/PyTorch 资源&教程&项目.md: -------------------------------------------------------------------------------- 1 | # PyTorch 资源&教程&项目 2 | 3 | # 教程 4 | 5 | 6 | - [Pytorch 官方中文文档](https://pytorch-cn.readthedocs.io/zh/latest/) 7 | - [英文文档](https://pytorch.org/docs/stable/index.html) 8 | - [官方教程](https://pytorch.org/tutorials/index.html) 9 | - [x] [DEEP LEARNING WITH PYTORCH: A 60 MINUTE BLITZ](https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html)--官方版本教程 10 | - [pytorch-tutorial](https://github.com/yunjey/pytorch-tutorial)--这个资源为深度学习研究人员提供了学习PyTorch的教程代码大多数模型都使用少于30行代码实现。 在开始本教程之前,建议先看完 Pytorch 官方教程。 11 | - [Awesome-pytorch-list](https://github.com/bharathgs/Awesome-pytorch-list)--Github 的 Awesome系列Pytorch版! 12 | - [《Pytorch模型训练实用教程》中配套代码](https://github.com/tensor-yu/PyTorch_Tutorial) 13 | - [Deep Tutorials for PyTorch](https://github.com/sgrvinod/Deep-Tutorials-for-PyTorch):一系列基于 Pytorch 的教程,目前包括 caption、sequence labeling、object detection 和 Text Classification, 复现对应领域的某篇论文的代码 14 | - [Awesome-PyTorch-Chinese](https://github.com/INTERMT/Awesome-PyTorch-Chinese):【干货】史上最全的PyTorch学习资源汇总 15 | - [LIS-YNP (Life Is Short-You Need Pytorch)](https://github.com/Eurus-Holmes/LIS-YNP) 16 | - [汇总 | 基于Pytorch的YOLO目标检测项目工程大合集](https://mp.weixin.qq.com/s/it1RQ-HxIcrL8r7M6eyURA) 17 | - [ritchieng/the-incredible-pytorch](https://github.com/ritchieng/the-incredible-pytorch):有关 PyTorch 的各种教程、项目、视频等资源。 18 | 19 | - [microsoft/computervision-recipes](https://github.com/microsoft/computervision-recipes):微软出品,基于 PyTorch 的各种 CV 任务的教程。 20 | 21 | - [donnyyou/torchcv](https://github.com/donnyyou/torchcv):基于 PyTorch 的 CV 模型框架,包含图像分类、语义分割、目标检测、姿态检测、实例分割、生成对抗网络等任务中的多个常见模型。 22 | 23 | 24 | 25 | 26 | 27 | --- 28 | 29 | # 资源 30 | 31 | 32 | - [Pytorch 讨论社区](https://discuss.pytorch.org/) 33 | 34 | 35 | 36 | 37 | --- 38 | 39 | # Github项目 40 | 41 | 42 | - [pretrained-models.pytorch](https://github.com/Cadene/pretrained-models.pytorch)--Pretrained ConvNets for pytorch: NASNet, ResNeXt, ResNet, InceptionV4, InceptionResnetV2, Xception, DPN, etc. 43 | - [Image_Segmentation](https://github.com/LeeJunHyun/Image_Segmentation)--pytorch Implementation of U-Net, R2U-Net, Attention U-Net, Attention R2U-Net. 44 | - [pytorch-template](https://github.com/lyakaap/pytorch-template) 45 | - [pytorch_image_classification](https://github.com/hysts/pytorch_image_classification):复现各种分类网络、性能对比结果和论文 46 | - [Pytorch 实现头发分割](https://github.com/YBIGTA/pytorch-hair-segmentation) 47 | - [PyTorch 文本分类教程](https://github.com/sgrvinod/a-PyTorch-Tutorial-to-Text-Classification) 48 | - [pytorch-image-models](https://github.com/rwightman/pytorch-image-models):各种网络模型 49 | - [pytorch文本生成工具](https://github.com/asyml/texar-pytorch) 50 | - [Pytorch 网络结构可视化](https://mp.weixin.qq.com/s/EJDSJYsXAxBzVom3NSBRnA),[Github](https://github.com/microsoft/tensorwatch) 51 | - [Python Summary](https://github.com/sksq96/pytorch-summary) 52 | - [可视化网络层输出的热力图--GradCAM](https://github.com/jacobgil/pytorch-grad-cam) 53 | - [PyTorch模型类激活图浏览器](https://github.com/frgfm/torch-cam)--实现了CAM, Grad-CAM, Grad-CAM++, Smooth Grad-CAM++, Score-CAM 54 | 55 | --- 56 | 57 | # 文章 58 | 59 | 60 | - [ ] [PyTorch Cookbook(常用代码段整理合集)](https://zhuanlan.zhihu.com/p/59205847?) 61 | - [ ] [PyTorch Tricks 集锦](https://mp.weixin.qq.com/s/wSnaLKFKz8lbv1ru5UhVlA) 62 | - [ ] [PyTorch常见的坑汇总](https://mp.weixin.qq.com/s/JLMe0lJ6SAhOtrLR9IYEjQ) 63 | - [ ] [在深度学习中喂饱 GPU](https://mp.weixin.qq.com/s/r8K3jZsmpRnSoVijKXZkMA)--加快加载数据的速度 64 | - [ ] [PyTorch中模型的可复现性](https://mp.weixin.qq.com/s/BHxcq0dd6Q3Dgxqe1gLnDg) 65 | - [ ] [pytorch实用工具总结](https://mp.weixin.qq.com/s/z-kmDpKWcvW8R9h12viXTg)--实现各种功能的代码,包括查看模型层数、模型参数总量、模型计算量等 66 | - [x] [加速PyTorch](https://towardsdatascience.com/speed-up-your-algorithms-part-1-pytorch-56d8a4ae7051) 67 | - [x] [用Pytorch超快速训练神经网络的九个技巧](https://towardsdatascience.com/9-tips-for-training-lightning-fast-neural-networks-in-pytorch-8e63a502f565) 68 | 69 | 70 | 71 | 72 | 73 | --- 74 | 75 | # 课程 76 | 77 | - [5天入门深度学习课程](https://mlelarge.github.io/dataflowr-web/cea_edf_inria.html),Github:https://github.com/mlelarge/dataflowr 78 | -------------------------------------------------------------------------------- /Pytorch/create_landmark_dataset.py: -------------------------------------------------------------------------------- 1 | """Create a sample face landmarks dataset. 2 | 3 | Adapted from dlib/python_examples/face_landmark_detection.py 4 | See this file for more explanation. 5 | 6 | Download a trained facial shape predictor from: 7 | http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2 8 | """ 9 | import dlib 10 | import glob 11 | import csv 12 | from skimage import io 13 | 14 | detector = dlib.get_frontal_face_detector() 15 | predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat') 16 | num_landmarks = 68 17 | 18 | with open('face_landmarks.csv', 'w', newline='') as csvfile: 19 | csv_writer = csv.writer(csvfile) 20 | 21 | header = ['image_name'] 22 | for i in range(num_landmarks): 23 | header += ['part_{}_x'.format(i), 'part_{}_y'.format(i)] 24 | 25 | csv_writer.writerow(header) 26 | 27 | for f in glob.glob('*.jpg'): 28 | img = io.imread(f) 29 | dets = detector(img, 1) # face detection 30 | 31 | # ignore all the files with no or more than one faces detected. 32 | if len(dets) == 1: 33 | row = [f] 34 | 35 | d = dets[0] 36 | # Get the landmarks/parts for the face in box d. 37 | shape = predictor(img, d) 38 | for i in range(num_landmarks): 39 | part_i_x = shape.part(i).x 40 | part_i_y = shape.part(i).y 41 | row += [part_i_x, part_i_y] 42 | 43 | csv_writer.writerow(row) 44 | -------------------------------------------------------------------------------- /Pytorch/practise/autograd.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Autograd: automatic differentiation\n", 8 | "\n", 9 | "`autograd` 是 Pytorch 中对于所有神经网络来说非常重要的库,这个库提供了对 Tensors 的所有操作的自动微分操作。" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "### Tensor" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 1, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "import torch" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "创建一个 tensor, 并让 `requires_grad=True`" 33 | ] 34 | }, 35 | { 36 | "cell_type": "code", 37 | "execution_count": 2, 38 | "metadata": {}, 39 | "outputs": [ 40 | { 41 | "name": "stdout", 42 | "output_type": "stream", 43 | "text": [ 44 | "tensor([[1., 1.],\n", 45 | " [1., 1.]], requires_grad=True)\n" 46 | ] 47 | } 48 | ], 49 | "source": [ 50 | "x = torch.ones(2, 2, requires_grad=True)\n", 51 | "print(x)" 52 | ] 53 | }, 54 | { 55 | "cell_type": "markdown", 56 | "metadata": {}, 57 | "source": [ 58 | "对 tensor 进行任意一个操作" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 4, 64 | "metadata": {}, 65 | "outputs": [ 66 | { 67 | "name": "stdout", 68 | "output_type": "stream", 69 | "text": [ 70 | "tensor([[3., 3.],\n", 71 | " [3., 3.]], grad_fn=)\n" 72 | ] 73 | } 74 | ], 75 | "source": [ 76 | "y = x + 2\n", 77 | "print(y)" 78 | ] 79 | }, 80 | { 81 | "cell_type": "markdown", 82 | "metadata": {}, 83 | "source": [ 84 | "`y` 是一个操作的结果,所以它带有操作 `grad_fn` " 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": 5, 90 | "metadata": {}, 91 | "outputs": [ 92 | { 93 | "name": "stdout", 94 | "output_type": "stream", 95 | "text": [ 96 | "\n" 97 | ] 98 | } 99 | ], 100 | "source": [ 101 | "print(y.grad_fn)" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": 7, 107 | "metadata": {}, 108 | "outputs": [ 109 | { 110 | "name": "stdout", 111 | "output_type": "stream", 112 | "text": [ 113 | "z= tensor([[27., 27.],\n", 114 | " [27., 27.]], grad_fn=)\n", 115 | "out= tensor(27., grad_fn=)\n" 116 | ] 117 | } 118 | ], 119 | "source": [ 120 | "z = y * y * 3\n", 121 | "out = z.mean()\n", 122 | "\n", 123 | "print('z=', z)\n", 124 | "print('out=', out)" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": 8, 130 | "metadata": {}, 131 | "outputs": [ 132 | { 133 | "name": "stdout", 134 | "output_type": "stream", 135 | "text": [ 136 | "False\n", 137 | "True\n", 138 | "\n" 139 | ] 140 | } 141 | ], 142 | "source": [ 143 | "a = torch.randn(2, 2)\n", 144 | "a = ((a * 3) / (a - 1))\n", 145 | "print(a.requires_grad)\n", 146 | "a.requires_grad_(True)\n", 147 | "print(a.requires_grad)\n", 148 | "b = (a * a).sum()\n", 149 | "print(b.grad_fn)" 150 | ] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "metadata": {}, 155 | "source": [ 156 | "### Gradients" 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": 9, 162 | "metadata": {}, 163 | "outputs": [], 164 | "source": [ 165 | "out.backward()" 166 | ] 167 | }, 168 | { 169 | "cell_type": "markdown", 170 | "metadata": {}, 171 | "source": [ 172 | "输出 d(out)/dx, out=z.mean(), z= y\\*y\\*3, y=x+2" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": 10, 178 | "metadata": {}, 179 | "outputs": [ 180 | { 181 | "name": "stdout", 182 | "output_type": "stream", 183 | "text": [ 184 | "tensor([[4.5000, 4.5000],\n", 185 | " [4.5000, 4.5000]])\n" 186 | ] 187 | } 188 | ], 189 | "source": [ 190 | "print(x.grad)" 191 | ] 192 | }, 193 | { 194 | "cell_type": "code", 195 | "execution_count": 11, 196 | "metadata": {}, 197 | "outputs": [ 198 | { 199 | "name": "stdout", 200 | "output_type": "stream", 201 | "text": [ 202 | "tensor([ 237.5009, 1774.2396, 274.0625], grad_fn=)\n" 203 | ] 204 | } 205 | ], 206 | "source": [ 207 | "x = torch.randn(3, requires_grad=True)\n", 208 | "\n", 209 | "y = x * 2\n", 210 | "while y.data.norm() < 1000:\n", 211 | " y = y * 2\n", 212 | "\n", 213 | "print(y)" 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": null, 219 | "metadata": {}, 220 | "outputs": [], 221 | "source": [ 222 | "" 223 | ] 224 | }, 225 | { 226 | "cell_type": "code", 227 | "execution_count": 12, 228 | "metadata": {}, 229 | "outputs": [ 230 | { 231 | "name": "stdout", 232 | "output_type": "stream", 233 | "text": [ 234 | "tensor([ 102.4000, 1024.0000, 0.1024])\n" 235 | ] 236 | } 237 | ], 238 | "source": [ 239 | "v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)\n", 240 | "y.backward(v)\n", 241 | "\n", 242 | "print(x.grad)" 243 | ] 244 | }, 245 | { 246 | "cell_type": "code", 247 | "execution_count": null, 248 | "metadata": {}, 249 | "outputs": [], 250 | "source": [ 251 | "" 252 | ] 253 | }, 254 | { 255 | "cell_type": "code", 256 | "execution_count": 13, 257 | "metadata": {}, 258 | "outputs": [ 259 | { 260 | "name": "stdout", 261 | "output_type": "stream", 262 | "text": [ 263 | "True\n", 264 | "True\n", 265 | "False\n" 266 | ] 267 | } 268 | ], 269 | "source": [ 270 | "print(x.requires_grad)\n", 271 | "print((x ** 2).requires_grad)\n", 272 | "\n", 273 | "with torch.no_grad():\n", 274 | " print((x ** 2).requires_grad)" 275 | ] 276 | }, 277 | { 278 | "cell_type": "code", 279 | "execution_count": null, 280 | "metadata": {}, 281 | "outputs": [], 282 | "source": [ 283 | "" 284 | ] 285 | }, 286 | { 287 | "cell_type": "code", 288 | "execution_count": null, 289 | "metadata": {}, 290 | "outputs": [], 291 | "source": [ 292 | "" 293 | ] 294 | } 295 | ], 296 | "metadata": { 297 | "kernelspec": { 298 | "display_name": "Python 3", 299 | "language": "python", 300 | "name": "python3" 301 | }, 302 | "language_info": { 303 | "codemirror_mode": { 304 | "name": "ipython", 305 | "version": 3.0 306 | }, 307 | "file_extension": ".py", 308 | "mimetype": "text/x-python", 309 | "name": "python", 310 | "nbconvert_exporter": "python", 311 | "pygments_lexer": "ipython3", 312 | "version": "3.6.6" 313 | } 314 | }, 315 | "nbformat": 4, 316 | "nbformat_minor": 0 317 | } -------------------------------------------------------------------------------- /Pytorch/practise/basic_practise.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "collapsed": true 7 | }, 8 | "source": [ 9 | "### Tensors\n", 10 | "\n", 11 | "Tensors 类似于 numpy 中的多维数组(ndarrays),但它还可以用于 GPU 中实现计算的加速。" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 1, 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "from __future__ import print_function\n", 21 | "import torch" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "创建一个未初始化的 5*3 矩阵" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 2, 34 | "metadata": {}, 35 | "outputs": [ 36 | { 37 | "name": "stdout", 38 | "output_type": "stream", 39 | "text": [ 40 | "tensor([[9.2737e-41, 8.9074e-01, 1.9286e-37],\n", 41 | " [1.7228e-34, 5.7064e+01, 9.2737e-41],\n", 42 | " [2.2803e+02, 1.9288e-37, 1.7228e-34],\n", 43 | " [1.4609e+04, 9.2737e-41, 5.8375e+04],\n", 44 | " [1.9290e-37, 1.7228e-34, 3.7402e+06]])\n" 45 | ] 46 | } 47 | ], 48 | "source": [ 49 | "x = torch.empty(5, 3)\n", 50 | "print(x)" 51 | ] 52 | }, 53 | { 54 | "cell_type": "markdown", 55 | "metadata": {}, 56 | "source": [ 57 | "创建一个随机初始化的 5*3 矩阵" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 3, 63 | "metadata": {}, 64 | "outputs": [ 65 | { 66 | "name": "stdout", 67 | "output_type": "stream", 68 | "text": [ 69 | "tensor([[0.4311, 0.2798, 0.8444],\n", 70 | " [0.0829, 0.9029, 0.8463],\n", 71 | " [0.7139, 0.4225, 0.5623],\n", 72 | " [0.7642, 0.0329, 0.8816],\n", 73 | " [1.0000, 0.9830, 0.9256]])\n" 74 | ] 75 | } 76 | ], 77 | "source": [ 78 | "rand_x = torch.rand(5, 3)\n", 79 | "print(rand_x)" 80 | ] 81 | }, 82 | { 83 | "cell_type": "markdown", 84 | "metadata": {}, 85 | "source": [ 86 | "创建一个数值皆是 0,类型为 long 的矩阵" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": 4, 92 | "metadata": {}, 93 | "outputs": [ 94 | { 95 | "name": "stdout", 96 | "output_type": "stream", 97 | "text": [ 98 | "tensor([[0, 0, 0],\n", 99 | " [0, 0, 0],\n", 100 | " [0, 0, 0],\n", 101 | " [0, 0, 0],\n", 102 | " [0, 0, 0]])\n" 103 | ] 104 | } 105 | ], 106 | "source": [ 107 | "zero_x = torch.zeros(5, 3, dtype=torch.long)\n", 108 | "print(zero_x)" 109 | ] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "metadata": {}, 114 | "source": [ 115 | "创建一个 tensor,初始化方式是给定数值" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": 5, 121 | "metadata": {}, 122 | "outputs": [ 123 | { 124 | "name": "stdout", 125 | "output_type": "stream", 126 | "text": [ 127 | "tensor([5.5000, 3.0000])\n" 128 | ] 129 | } 130 | ], 131 | "source": [ 132 | "tensor1 = torch.tensor([5.5, 3])\n", 133 | "print(tensor1)" 134 | ] 135 | }, 136 | { 137 | "cell_type": "markdown", 138 | "metadata": {}, 139 | "source": [ 140 | "根据已存在的 tensor 创建新的 tensor" 141 | ] 142 | }, 143 | { 144 | "cell_type": "code", 145 | "execution_count": 6, 146 | "metadata": {}, 147 | "outputs": [ 148 | { 149 | "name": "stdout", 150 | "output_type": "stream", 151 | "text": [ 152 | "tensor([[1., 1., 1.],\n", 153 | " [1., 1., 1.],\n", 154 | " [1., 1., 1.],\n", 155 | " [1., 1., 1.],\n", 156 | " [1., 1., 1.]], dtype=torch.float64)\n" 157 | ] 158 | } 159 | ], 160 | "source": [ 161 | "tensor2 = tensor1.new_ones(5, 3, dtype=torch.double) # new_* 方法需要输入 tensor 大小\n", 162 | "print(tensor2)" 163 | ] 164 | }, 165 | { 166 | "cell_type": "markdown", 167 | "metadata": {}, 168 | "source": [ 169 | "修改数值类型,但有相同的 size" 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": 7, 175 | "metadata": {}, 176 | "outputs": [ 177 | { 178 | "name": "stdout", 179 | "output_type": "stream", 180 | "text": [ 181 | "tensor3: tensor([[-0.4491, -0.2634, -0.0040],\n", 182 | " [-0.1624, 0.4475, -0.8407],\n", 183 | " [-0.6539, -1.2772, 0.6060],\n", 184 | " [ 0.2304, 0.0879, -0.3876],\n", 185 | " [ 1.2900, -0.7475, -1.8212]])\n" 186 | ] 187 | } 188 | ], 189 | "source": [ 190 | "tensor3 = torch.randn_like(tensor2, dtype=torch.float)\n", 191 | "print('tensor3: ', tensor3)" 192 | ] 193 | }, 194 | { 195 | "cell_type": "markdown", 196 | "metadata": {}, 197 | "source": [ 198 | "获取 tensor 的 size" 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "execution_count": 8, 204 | "metadata": {}, 205 | "outputs": [ 206 | { 207 | "name": "stdout", 208 | "output_type": "stream", 209 | "text": [ 210 | "torch.Size([5, 3])\n" 211 | ] 212 | } 213 | ], 214 | "source": [ 215 | "print(tensor3.size())" 216 | ] 217 | }, 218 | { 219 | "cell_type": "markdown", 220 | "metadata": {}, 221 | "source": [ 222 | "**注意**: torch.Size 实际上是元组(tuple)类型,所以支持所有的元组操作" 223 | ] 224 | }, 225 | { 226 | "cell_type": "markdown", 227 | "metadata": {}, 228 | "source": [ 229 | "### Operations" 230 | ] 231 | }, 232 | { 233 | "cell_type": "markdown", 234 | "metadata": {}, 235 | "source": [ 236 | "加法操作" 237 | ] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "execution_count": 9, 242 | "metadata": {}, 243 | "outputs": [ 244 | { 245 | "name": "stdout", 246 | "output_type": "stream", 247 | "text": [ 248 | "tensor3 + tensor4= tensor([[ 0.1000, 0.1325, 0.0461],\n", 249 | " [ 0.4731, 0.4523, -0.7517],\n", 250 | " [ 0.2995, -0.9576, 1.4906],\n", 251 | " [ 1.0461, 0.7557, -0.0187],\n", 252 | " [ 2.2446, -0.3473, -1.0873]])\n", 253 | "tensor3 + tensor4= tensor([[ 0.1000, 0.1325, 0.0461],\n", 254 | " [ 0.4731, 0.4523, -0.7517],\n", 255 | " [ 0.2995, -0.9576, 1.4906],\n", 256 | " [ 1.0461, 0.7557, -0.0187],\n", 257 | " [ 2.2446, -0.3473, -1.0873]])\n" 258 | ] 259 | } 260 | ], 261 | "source": [ 262 | "tensor4 = torch.rand(5, 3)\n", 263 | "print('tensor3 + tensor4= ', tensor3 + tensor4)\n", 264 | "print('tensor3 + tensor4= ', torch.add(tensor3, tensor4))" 265 | ] 266 | }, 267 | { 268 | "cell_type": "markdown", 269 | "metadata": {}, 270 | "source": [ 271 | "新声明一个 tensor 变量保存加法操作的结果" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": 10, 277 | "metadata": {}, 278 | "outputs": [ 279 | { 280 | "name": "stdout", 281 | "output_type": "stream", 282 | "text": [ 283 | "add result= tensor([[ 0.1000, 0.1325, 0.0461],\n", 284 | " [ 0.4731, 0.4523, -0.7517],\n", 285 | " [ 0.2995, -0.9576, 1.4906],\n", 286 | " [ 1.0461, 0.7557, -0.0187],\n", 287 | " [ 2.2446, -0.3473, -1.0873]])\n" 288 | ] 289 | } 290 | ], 291 | "source": [ 292 | "result = torch.empty(5, 3)\n", 293 | "torch.add(tensor3, tensor4, out=result)\n", 294 | "print('add result= ', result)" 295 | ] 296 | }, 297 | { 298 | "cell_type": "markdown", 299 | "metadata": {}, 300 | "source": [ 301 | "直接修改变量" 302 | ] 303 | }, 304 | { 305 | "cell_type": "code", 306 | "execution_count": 11, 307 | "metadata": {}, 308 | "outputs": [ 309 | { 310 | "name": "stdout", 311 | "output_type": "stream", 312 | "text": [ 313 | "tensor3= tensor([[ 0.1000, 0.1325, 0.0461],\n", 314 | " [ 0.4731, 0.4523, -0.7517],\n", 315 | " [ 0.2995, -0.9576, 1.4906],\n", 316 | " [ 1.0461, 0.7557, -0.0187],\n", 317 | " [ 2.2446, -0.3473, -1.0873]])\n" 318 | ] 319 | } 320 | ], 321 | "source": [ 322 | "tensor3.add_(tensor4)\n", 323 | "print('tensor3= ', tensor3)" 324 | ] 325 | }, 326 | { 327 | "cell_type": "markdown", 328 | "metadata": {}, 329 | "source": [ 330 | "**注意**:可以改变 tensor 变量的操作都带有一个后缀 `_`, 例如 x.copy_(y), x.t_() 都可以改变 x 变量" 331 | ] 332 | }, 333 | { 334 | "cell_type": "markdown", 335 | "metadata": {}, 336 | "source": [ 337 | "可以用类似 numpy 的索引操作来访问 tensor 的某一维, 比如访问 tensor3 第一列数据" 338 | ] 339 | }, 340 | { 341 | "cell_type": "code", 342 | "execution_count": 12, 343 | "metadata": {}, 344 | "outputs": [ 345 | { 346 | "name": "stdout", 347 | "output_type": "stream", 348 | "text": [ 349 | "tensor([0.1000, 0.4731, 0.2995, 1.0461, 2.2446])\n" 350 | ] 351 | } 352 | ], 353 | "source": [ 354 | "print(tensor3[:, 0])" 355 | ] 356 | }, 357 | { 358 | "cell_type": "markdown", 359 | "metadata": {}, 360 | "source": [ 361 | "修改 tensor 大小,可以采用方法 `torch.view`" 362 | ] 363 | }, 364 | { 365 | "cell_type": "code", 366 | "execution_count": 14, 367 | "metadata": {}, 368 | "outputs": [ 369 | { 370 | "name": "stdout", 371 | "output_type": "stream", 372 | "text": [ 373 | "torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])\n" 374 | ] 375 | } 376 | ], 377 | "source": [ 378 | "x = torch.randn(4, 4)\n", 379 | "y = x.view(16)\n", 380 | "# -1 表示除给定维度外的其余维度的乘积\n", 381 | "z = x.view(-1, 8)\n", 382 | "print(x.size(), y.size(), z.size())" 383 | ] 384 | }, 385 | { 386 | "cell_type": "markdown", 387 | "metadata": {}, 388 | "source": [ 389 | "如果 tensor 仅有一个元素,可以采用 `.item()` 来获取类似 Python 中整数类型的数值" 390 | ] 391 | }, 392 | { 393 | "cell_type": "code", 394 | "execution_count": 15, 395 | "metadata": {}, 396 | "outputs": [ 397 | { 398 | "name": "stdout", 399 | "output_type": "stream", 400 | "text": [ 401 | "tensor([0.4549])\n", 402 | "0.4549027979373932\n" 403 | ] 404 | } 405 | ], 406 | "source": [ 407 | "x = torch.randn(1)\n", 408 | "print(x)\n", 409 | "print(x.item())" 410 | ] 411 | }, 412 | { 413 | "cell_type": "code", 414 | "execution_count": null, 415 | "metadata": {}, 416 | "outputs": [], 417 | "source": [ 418 | "" 419 | ] 420 | }, 421 | { 422 | "cell_type": "markdown", 423 | "metadata": {}, 424 | "source": [ 425 | "### Numpy\n", 426 | "\n", 427 | "tensor 和 numpy 的 array 相互转换,并且如果是在 cpu 上的操作,两者转换会共享内存,即改变一个变量同时也改变另一个。" 428 | ] 429 | }, 430 | { 431 | "cell_type": "markdown", 432 | "metadata": {}, 433 | "source": [ 434 | "#### Tensor to Numpy array" 435 | ] 436 | }, 437 | { 438 | "cell_type": "code", 439 | "execution_count": 16, 440 | "metadata": {}, 441 | "outputs": [ 442 | { 443 | "name": "stdout", 444 | "output_type": "stream", 445 | "text": [ 446 | "tensor([1., 1., 1., 1., 1.])\n" 447 | ] 448 | } 449 | ], 450 | "source": [ 451 | "a = torch.ones(5)\n", 452 | "print(a)" 453 | ] 454 | }, 455 | { 456 | "cell_type": "code", 457 | "execution_count": 17, 458 | "metadata": {}, 459 | "outputs": [ 460 | { 461 | "name": "stdout", 462 | "output_type": "stream", 463 | "text": [ 464 | "[1. 1. 1. 1. 1.]\n" 465 | ] 466 | } 467 | ], 468 | "source": [ 469 | "b = a.numpy()\n", 470 | "print(b)" 471 | ] 472 | }, 473 | { 474 | "cell_type": "markdown", 475 | "metadata": {}, 476 | "source": [ 477 | "看看改变其中一个变量,另一个变量的改变" 478 | ] 479 | }, 480 | { 481 | "cell_type": "code", 482 | "execution_count": 18, 483 | "metadata": {}, 484 | "outputs": [ 485 | { 486 | "name": "stdout", 487 | "output_type": "stream", 488 | "text": [ 489 | "tensor([2., 2., 2., 2., 2.])\n", 490 | "[2. 2. 2. 2. 2.]\n" 491 | ] 492 | } 493 | ], 494 | "source": [ 495 | "a.add_(1)\n", 496 | "print(a)\n", 497 | "print(b)" 498 | ] 499 | }, 500 | { 501 | "cell_type": "markdown", 502 | "metadata": {}, 503 | "source": [ 504 | "#### Numpy Array to Torch Tensor" 505 | ] 506 | }, 507 | { 508 | "cell_type": "code", 509 | "execution_count": 19, 510 | "metadata": {}, 511 | "outputs": [ 512 | { 513 | "name": "stdout", 514 | "output_type": "stream", 515 | "text": [ 516 | "[2. 2. 2. 2. 2.]\n", 517 | "tensor([2., 2., 2., 2., 2.], dtype=torch.float64)\n" 518 | ] 519 | } 520 | ], 521 | "source": [ 522 | "import numpy as np\n", 523 | "a = np.ones(5)\n", 524 | "b = torch.from_numpy(a)\n", 525 | "np.add(a, 1, out=a)\n", 526 | "print(a)\n", 527 | "print(b)" 528 | ] 529 | }, 530 | { 531 | "cell_type": "markdown", 532 | "metadata": {}, 533 | "source": [ 534 | "在 CPU 上,除了 CharTensor 外,都支持和 Numpy 的 array 的互相转换" 535 | ] 536 | }, 537 | { 538 | "cell_type": "markdown", 539 | "metadata": {}, 540 | "source": [ 541 | "### CUDA Tensors\n", 542 | "\n", 543 | "Tensors 可以通过采用 `.to` 方法来转换到不同设备上" 544 | ] 545 | }, 546 | { 547 | "cell_type": "code", 548 | "execution_count": 20, 549 | "metadata": {}, 550 | "outputs": [ 551 | { 552 | "name": "stdout", 553 | "output_type": "stream", 554 | "text": [ 555 | "tensor([1.4549], device='cuda:0')\n", 556 | "tensor([1.4549], dtype=torch.float64)\n" 557 | ] 558 | } 559 | ], 560 | "source": [ 561 | "# 当 CUDA 可用的时候,可运行这段代码,采用 torch.device() 来改变 tensors 是否在 GPU 上进行计算操作\n", 562 | "if torch.cuda.is_available():\n", 563 | " device = torch.device(\"cuda\") # 定义一个 CUDA 设备对象\n", 564 | " y = torch.ones_like(x, device=device) # 显示创建在 GPU 上的一个 tensor\n", 565 | " x = x.to(device) # 也可以采用 .to(\"cuda\")\n", 566 | " z = x + y\n", 567 | " print(z)\n", 568 | " print(z.to(\"cpu\", torch.double)) # .to() 方法也可以改变数值类型" 569 | ] 570 | }, 571 | { 572 | "cell_type": "code", 573 | "execution_count": null, 574 | "metadata": {}, 575 | "outputs": [], 576 | "source": [ 577 | "" 578 | ] 579 | } 580 | ], 581 | "metadata": { 582 | "kernelspec": { 583 | "display_name": "Python 3", 584 | "language": "python", 585 | "name": "python3" 586 | }, 587 | "language_info": { 588 | "codemirror_mode": { 589 | "name": "ipython", 590 | "version": 3.0 591 | }, 592 | "file_extension": ".py", 593 | "mimetype": "text/x-python", 594 | "name": "python", 595 | "nbconvert_exporter": "python", 596 | "pygments_lexer": "ipython3", 597 | "version": "3.6.6" 598 | } 599 | }, 600 | "nbformat": 4, 601 | "nbformat_minor": 0 602 | } -------------------------------------------------------------------------------- /Pytorch/practise/basic_practise.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*-coding:utf-8 -*- 3 | """ 4 | 5 | @Author: Luocai 6 | @file: basic_practise.py 7 | @time: 2019/05/06 20:51 8 | @desc: 9 | 10 | 11 | """ 12 | from __future__ import print_function 13 | import torch 14 | 15 | ############# 16 | # Tensors # 17 | ############# 18 | 19 | # 创建一个未初始化的 5*3 矩阵 20 | x = torch.empty(5, 3) 21 | print(x) 22 | # 创建一个随机初始化的 5*3 矩阵 23 | rand_x = torch.rand(5, 3) 24 | print(rand_x) 25 | # 创建一个数值为0,类型为 long 的矩阵 26 | zero_x = torch.zeros(5, 3, dtype=torch.long) 27 | print(zero_x) 28 | # 创建一个 tensor,初始化方式是当前数值 29 | tensor1 = torch.tensor([5.5, 3]) 30 | print(tensor1) 31 | # 根据已存在的 tensor 创建新的 tensor 32 | tensor2 = tensor1.new_ones(5, 3, dtype=torch.double) # new_* 方法需要输入 tensor 大小 33 | print(tensor2) 34 | # 修改数值类型,但有相同的 size 35 | tensor3 = torch.randn_like(tensor2, dtype=torch.float) 36 | print('tensor3: ', tensor3) 37 | # get size 38 | print(tensor3.size()) 39 | 40 | ################ 41 | # Operations # 42 | ################ 43 | 44 | # 加法操作 45 | tensor4 = torch.rand(5, 3) 46 | print('tensor3 + tensor4= ', tensor3 + tensor4) 47 | print('tensor3 + tensor4= ', torch.add(tensor3, tensor4)) 48 | # 给定加法结果的 tensor 变量 49 | result = torch.empty(5, 3) 50 | torch.add(tensor3, tensor4, out=result) 51 | print('add result= ', result) 52 | # 直接修改 tensor 变量 53 | tensor3.add_(tensor4) 54 | print('tensor3= ', tensor3) 55 | # 可以用类似 numpy 的索引操作来访问 tensor 的某一维, 比如访问 tensor3 第一列数据 56 | print(tensor3[:, 0]) 57 | 58 | # 修改 tensor 大小,torch.view() 59 | -------------------------------------------------------------------------------- /Pytorch/practise/neural_network.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "### 神经网络\n", 8 | "\n", 9 | "神经网络主要通过调用 `torch.nn` 包来构建,并采用`nn.Module`来构建网络层,调用其方法 `forward(input)` 得到输出 `output` " 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "#### 网络定义" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 1, 22 | "metadata": {}, 23 | "outputs": [ 24 | { 25 | "name": "stdout", 26 | "output_type": "stream", 27 | "text": [ 28 | "Net(\n", 29 | " (conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))\n", 30 | " (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))\n", 31 | " (fc1): Linear(in_features=400, out_features=120, bias=True)\n", 32 | " (fc2): Linear(in_features=120, out_features=84, bias=True)\n", 33 | " (fc3): Linear(in_features=84, out_features=10, bias=True)\n", 34 | ")\n" 35 | ] 36 | } 37 | ], 38 | "source": [ 39 | "import torch\n", 40 | "import torch.nn as nn\n", 41 | "import torch.nn.functional as F\n", 42 | "\n", 43 | "class Net(nn.Module):\n", 44 | " \n", 45 | " def __init__(self):\n", 46 | " super(Net, self).__init__()\n", 47 | " # 输入图像是单通道,conv1 kenrnel size=5*5,输出通道 6\n", 48 | " self.conv1 = nn.Conv2d(1, 6, 5)\n", 49 | " # conv2 kernel size=5*5, 输出通道 16\n", 50 | " self.conv2 = nn.Conv2d(6, 16, 5)\n", 51 | " # 全连接层\n", 52 | " self.fc1 = nn.Linear(16*5*5, 120)\n", 53 | " self.fc2 = nn.Linear(120, 84)\n", 54 | " self.fc3 = nn.Linear(84, 10)\n", 55 | " \n", 56 | " def forward(self, x):\n", 57 | " # max-pooling 采用一个 (2,2) 的滑动窗口\n", 58 | " x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))\n", 59 | " # 核(kernel)大小是方形的话,可仅定义一个数字,如 (2,2) 用 2 即可\n", 60 | " x = F.max_pool2d(F.relu(self.conv2(x)), 2)\n", 61 | " x = x.view(-1, self.num_flat_features(x))\n", 62 | " x = F.relu(self.fc1(x))\n", 63 | " x = F.relu(self.fc2(x))\n", 64 | " x = self.fc3(x)\n", 65 | " return x\n", 66 | " \n", 67 | " def num_flat_features(self, x):\n", 68 | " # 除了 batch 维度外的所有维度\n", 69 | " size = x.size()[1:]\n", 70 | " num_features = 1\n", 71 | " for s in size:\n", 72 | " num_features *= s\n", 73 | " return num_features\n", 74 | "\n", 75 | "net = Net()\n", 76 | "print(net)" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": 2, 82 | "metadata": {}, 83 | "outputs": [ 84 | { 85 | "name": "stdout", 86 | "output_type": "stream", 87 | "text": [ 88 | "参数数量: 10\n", 89 | "第一个参数大小: torch.Size([6, 1, 5, 5])\n" 90 | ] 91 | } 92 | ], 93 | "source": [ 94 | "params = list(net.parameters())\n", 95 | "print('参数数量: ', len(params))\n", 96 | "# conv1.weight\n", 97 | "print('第一个参数大小: ', params[0].size())" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": 3, 103 | "metadata": {}, 104 | "outputs": [ 105 | { 106 | "name": "stdout", 107 | "output_type": "stream", 108 | "text": [ 109 | "tensor([[ 0.1005, 0.0263, 0.0013, -0.1157, -0.1197, -0.0141, 0.1425, -0.0521,\n", 110 | " 0.0689, 0.0220]], grad_fn=)\n" 111 | ] 112 | } 113 | ], 114 | "source": [ 115 | "# 随机定义一个变量输入网络\n", 116 | "input = torch.randn(1, 1, 32, 32)\n", 117 | "out = net(input)\n", 118 | "print(out)" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": 4, 124 | "metadata": {}, 125 | "outputs": [], 126 | "source": [ 127 | "# 清空所有参数的梯度缓存,然后计算随机梯度进行反向传播\n", 128 | "net.zero_grad()\n", 129 | "out.backward(torch.randn(1, 10))" 130 | ] 131 | }, 132 | { 133 | "cell_type": "markdown", 134 | "metadata": {}, 135 | "source": [ 136 | "#### 损失函数" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": 6, 142 | "metadata": {}, 143 | "outputs": [ 144 | { 145 | "name": "stdout", 146 | "output_type": "stream", 147 | "text": [ 148 | "tensor(0.6524, grad_fn=)\n" 149 | ] 150 | } 151 | ], 152 | "source": [ 153 | "output = net(input)\n", 154 | "# 定义伪标签\n", 155 | "target = torch.randn(10)\n", 156 | "# 调整大小,使得和 output 一样的 size\n", 157 | "target = target.view(1, -1)\n", 158 | "criterion = nn.MSELoss()\n", 159 | "\n", 160 | "loss = criterion(output, target)\n", 161 | "print(loss)" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": 10, 167 | "metadata": {}, 168 | "outputs": [ 169 | { 170 | "name": "stdout", 171 | "output_type": "stream", 172 | "text": [ 173 | "\n", 174 | "\n", 175 | "\n" 176 | ] 177 | } 178 | ], 179 | "source": [ 180 | "# MSELoss\n", 181 | "print(loss.grad_fn)\n", 182 | "# Linear layer\n", 183 | "print(loss.grad_fn.next_functions[0][0])\n", 184 | "# Relu\n", 185 | "print(loss.grad_fn.next_functions[0][0].next_functions[0][0])" 186 | ] 187 | }, 188 | { 189 | "cell_type": "markdown", 190 | "metadata": {}, 191 | "source": [ 192 | "#### 反向传播" 193 | ] 194 | }, 195 | { 196 | "cell_type": "code", 197 | "execution_count": 11, 198 | "metadata": {}, 199 | "outputs": [ 200 | { 201 | "name": "stdout", 202 | "output_type": "stream", 203 | "text": [ 204 | "conv1.bias.grad before backward\n", 205 | "tensor([0., 0., 0., 0., 0., 0.])\n", 206 | "conv1.bias.grad after backward\n", 207 | "tensor([ 0.0069, 0.0021, 0.0090, -0.0060, -0.0008, -0.0073])\n" 208 | ] 209 | } 210 | ], 211 | "source": [ 212 | "# 清空所有参数的梯度缓存\n", 213 | "net.zero_grad()\n", 214 | "print('conv1.bias.grad before backward')\n", 215 | "print(net.conv1.bias.grad)\n", 216 | "\n", 217 | "loss.backward()\n", 218 | "\n", 219 | "print('conv1.bias.grad after backward')\n", 220 | "print(net.conv1.bias.grad)" 221 | ] 222 | }, 223 | { 224 | "cell_type": "markdown", 225 | "metadata": {}, 226 | "source": [ 227 | "#### 更新权重" 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": 17, 233 | "metadata": {}, 234 | "outputs": [], 235 | "source": [ 236 | "# 简单实现权重的更新例子\n", 237 | "learning_rate = 0.01\n", 238 | "for f in net.parameters():\n", 239 | " f.data.sub_(f.grad.data * learning_rate)" 240 | ] 241 | }, 242 | { 243 | "cell_type": "code", 244 | "execution_count": 18, 245 | "metadata": {}, 246 | "outputs": [], 247 | "source": [ 248 | "import torch.optim as optim\n", 249 | "# 创建优化器\n", 250 | "optimizer = optim.SGD(net.parameters(), lr=0.01)\n", 251 | "\n", 252 | "# 在训练过程中执行下列操作\n", 253 | "optimizer.zero_grad() # 清空梯度缓存\n", 254 | "output = net(input)\n", 255 | "loss = criterion(output, target)\n", 256 | "loss.backward()\n", 257 | "# 更新权重\n", 258 | "optimizer.step()" 259 | ] 260 | }, 261 | { 262 | "cell_type": "code", 263 | "execution_count": null, 264 | "metadata": {}, 265 | "outputs": [], 266 | "source": [ 267 | "" 268 | ] 269 | } 270 | ], 271 | "metadata": { 272 | "kernelspec": { 273 | "display_name": "Python 3", 274 | "language": "python", 275 | "name": "python3" 276 | }, 277 | "language_info": { 278 | "codemirror_mode": { 279 | "name": "ipython", 280 | "version": 3.0 281 | }, 282 | "file_extension": ".py", 283 | "mimetype": "text/x-python", 284 | "name": "python", 285 | "nbconvert_exporter": "python", 286 | "pygments_lexer": "ipython3", 287 | "version": "3.6.6" 288 | } 289 | }, 290 | "nbformat": 4, 291 | "nbformat_minor": 0 292 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DeepLearning_Notes 2 | 3 | 联系方式: 4 | 5 | Github:https://github.com/ccc013 6 | 7 | 知乎专栏:[机器学习与计算机视觉](https://www.zhihu.com/column/c_1060581544644718592),[AI 论文笔记](https://www.zhihu.com/column/c_1364201355796656128) 8 | 9 | 微信公众号:**AI 算法笔记** 10 | 11 | ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210620155918137.jpeg#pic_center) 12 | 13 | 机器学习、深度学习的学习笔记、代码,包括教程、实战,以及论文阅读笔记和代码。 14 | 15 | 目录 16 | 17 | - [学习笔记](https://github.com/ccc013/DeepLearning_Notes#%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0) 18 | - [数学](https://github.com/ccc013/DeepLearning_Notes#%E6%95%B0%E5%AD%A6) 19 | - [论文笔记](https://github.com/ccc013/DeepLearning_Notes#%E8%AE%BA%E6%96%87%E7%AC%94%E8%AE%B0) 20 | - [教程、文章、网站等推荐](https://github.com/ccc013/DeepLearning_Notes/blob/master/%E8%B5%84%E6%BA%90%26%E6%95%99%E7%A8%8B%26%E9%A1%B9%E7%9B%AE.md) 21 | 22 | 23 | 24 | ## 学习笔记 25 | 26 | ### 数学 27 | 28 | 1. [线性代数的学习笔记](https://mp.weixin.qq.com/s/2JsQ09Ol3spPp8anb5GNZA) 29 | 2. [概率论的学习笔记](https://mp.weixin.qq.com/s/1WBJyxlwHeYlY9_xqUcQ-Q) 30 | 3. [数值计算&微积分的学习笔记](https://mp.weixin.qq.com/s/8vLuyvAXmztO2wG2ALc9yw) 31 | 32 | 33 | 34 | 35 | 36 | ------ 37 | 38 | ## 论文笔记 39 | 40 | 记录平时阅读的论文笔记 41 | 42 | 43 | 44 | ### Object Detection 45 | 46 | 47 | | Title | Paper | Code | My note | Discuss | 48 | | :-----------------------------------: | :------------------------------: | :----------------------------------------------------------: | ------------------------------------------------- | ------- | 49 | | Focal Loss for Dense Object Detection | https://arxiv.org/abs/1708.02002 | 官方 github:https://github.com/facebookresearch/detectron tensorflow:https://github.com/tensorflow/models https://github.com/fizyr/keras-retinanet https://github.com/yhenon/pytorch-retinanet | https://mp.weixin.qq.com/s/6ivSPTfF_h6eMyE7h18hUQ | | 50 | 51 | 52 | 53 | 54 | 55 | ------ 56 | 57 | ### OCR 58 | 59 | | Title | Paper | Code | My note | Discuss | 60 | | :----------------------------------------------------------: | :------------------------------: | :----------------------------: | ------------------------------------------------------------ | ------- | 61 | | An End-to-End Trainable Neural Network for Image-based Sequence Recognition and Its Application to Scene Text Recognition | https://arxiv.org/abs/1507.05717 | https://github.com/bgshih/crnn | https://mp.weixin.qq.com/s?__biz=MzkwMzEyNDgyMA==&mid=2247483793&idx=1&sn=c48d812a8fde3a39c90dc5a64375ff83&chksm=c09a43aaf7edcabc8b2dd7d959254daa8cb475b112be03ab735b94d9febdb90aaf22e760872d&token=1492400943&lang=zh_CN#rd | | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | ------ 70 | 71 | 72 | ### Fashion 73 | 74 | | Title | Paper | Code | My note | Discuss | 75 | | :----------------------------------------------------------: | :---------------------------------------------: | :------------------------------------------------------: | ------------------------------------------------------------ | --------------------------------------------------------- | 76 | | **FashionNet**:"FashionNet: Personalized Outfit Recommendation with Deep Neural Network" | https://www.ijcai.org/proceedings/2017/0239.pdf | / | https://github.com/ccc013/paper_reading/blob/master/fashion/FashionNet%20%E8%AE%BA%E6%96%87%E7%AC%94%E8%AE%B0.md | [issue](https://github.com/ccc013/paper_reading/issues/1) | 77 | | Learning Fashion Compatibility with Bidirectional LSTMs | https://arxiv.org/abs/1707.05691 | https://github.com/xthan/polyvore | https://mp.weixin.qq.com/s?__biz=MzkwMzEyNDgyMA==&mid=2247483874&idx=1&sn=ee302ebc030f0da7775328699af9128d&chksm=c09a43d9f7edcacf76df3961c438c9e3a627dc8a91aaafb6303f6b5345e3336dec77b90d296a&token=1862930471&lang=zh_CN#rd | | 78 | | Outfit Compatibility Prediction and Diagnosis with Multi-Layered Comparison Network | https://arxiv.org/abs/1907.11496 | https://github.com/WangXin93/fashion_compatibility_mcn | https://mp.weixin.qq.com/s/LTkP8RHfJHKHfcUNxqH2qw | | 79 | | Learning Similarity Conditions Without Explicit Supervision | https://arxiv.org/pdf/1908.08589.pdf | https://github.com/rxtan2/Learning-Similarity-Conditions | https://mp.weixin.qq.com/s?__biz=MzkwMzEyNDgyMA==&mid=2247483945&idx=1&sn=483d4244460e482c7cecda1dd7ea5d9e&chksm=c09a4012f7edc9046e009843d7094f869ff72d32a0168a20f8e8dd3245c08e1935c734f46ca7&token=1492400943&lang=zh_CN#rd | | 80 | | | | | | | 81 | 82 | 83 | 84 | ------ 85 | 86 | ### Recommend System 87 | 88 | | Title | Paper | Code | My note | Discuss | 89 | | :----------------------------------------------------------: | -------------------------------------------------------- | ------------------------------------------------------------ | ------- | ------- | 90 | | **DeepFM**:"DeepFM: A Factorization-Machine based Neural Network for CTR Prediction". | [paper](https://www.ijcai.org/proceedings/2017/0239.pdf) | [Tensorflow](https://github.com/ChenglongChen/tensorflow-DeepFM) [Pytorch](https://github.com/nzc/dnn_ctr) | | | 91 | | | | | | | 92 | | | | | | | -------------------------------------------------------------------------------- /Tutorials/machineLearning_Python/README.md: -------------------------------------------------------------------------------- 1 | 教程来自 https://www.pyimagesearch.com/2019/01/14/machine-learning-in-python/ 2 | 3 | 主要是采用 `scikit-learn` 和 `Keras` 两个库来实现以下几种常见的机器学习算法: 4 | 5 | - KNN 6 | - 朴素贝叶斯 7 | - 逻辑回归 8 | - SVM 9 | - 决策树 10 | - 随机森林 11 | - 感知机 12 | - 多层前向网络 13 | - CNNs 14 | 15 | 实验的数据集是: 16 | 17 | - **Iris(鸢尾花)** 数据集 18 | - **3-scenes 图像数据集**,从 [Modeling the shape of the scene: a holistic representation of the spatial envelope](http://people.csail.mit.edu/torralba/code/spatialenvelope/) 发布的 8 场景数据集采样得到 19 | 20 | 其中,`Iris` 数据集直接通过 `sklearn.datasets()` 导入 `load_iris()` 即可导入,而第二个数据集可以从原文中下载获取,或通过百度网盘下载,链接如下: 21 | 22 | 链接:https://pan.baidu.com/s/16Atyd6f9zrYZd1eQ3GlTLA 23 | 24 | 提取码:olj0 25 | 26 | -------------------------------------------------------------------------------- /Tutorials/machineLearning_Python/basic_cnn.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | @Time : 2019/3/16 12:05 5 | @Author : cai 6 | 7 | 教程来自 https://www.pyimagesearch.com/2019/01/14/machine-learning-in-python/ 8 | """ 9 | from keras.models import Sequential 10 | from keras.layers.convolutional import Conv2D 11 | from keras.layers.convolutional import MaxPooling2D 12 | from keras.layers.core import Activation 13 | from keras.layers.core import Flatten 14 | from keras.layers.core import Dense 15 | from keras.optimizers import Adam 16 | from sklearn.preprocessing import LabelBinarizer 17 | from sklearn.model_selection import train_test_split 18 | from sklearn.metrics import classification_report 19 | from PIL import Image 20 | from imutils import paths 21 | import numpy as np 22 | import argparse 23 | import os 24 | 25 | # 配置参数 26 | ap = argparse.ArgumentParser() 27 | ap.add_argument("-d", "--dataset", type=str, default="3scenes", 28 | help="path to directory containing the '3scenes' dataset") 29 | args = vars(ap.parse_args()) 30 | 31 | # 加载数据并提取特征 32 | print("[INFO] extracting image features...") 33 | imagePaths = paths.list_images(args['dataset']) 34 | data = [] 35 | labels = [] 36 | 37 | # 循环遍历所有的图片数据 38 | for imagePath in imagePaths: 39 | # 加载图片,然后调整成 32×32 大小,并做归一化到 [0,1] 40 | image = Image.open(imagePath) 41 | image = np.array(image.resize((32, 32))) / 255.0 42 | data.append(image) 43 | 44 | # 保存图片的标签信息 45 | label = imagePath.split(os.path.sep)[-2] 46 | labels.append(label) 47 | 48 | # 对标签编码,从字符串变为整型 49 | lb = LabelBinarizer() 50 | labels = lb.fit_transform(labels) 51 | 52 | # 划分训练集和测试集 53 | (trainX, testX, trainY, testY) = train_test_split(np.array(data), np.array(labels), test_size=0.25) 54 | 55 | # 定义 CNN 网络模型结构 56 | model = Sequential() 57 | model.add(Conv2D(8, (3, 3), padding="same", input_shape=(32, 32, 3))) 58 | model.add(Activation("relu")) 59 | model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2))) 60 | model.add(Conv2D(16, (3, 3), padding="same")) 61 | model.add(Activation("relu")) 62 | model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2))) 63 | model.add(Conv2D(32, (3, 3), padding="same")) 64 | model.add(Activation("relu")) 65 | model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2))) 66 | model.add(Flatten()) 67 | model.add(Dense(3)) 68 | model.add(Activation("softmax")) 69 | 70 | # 训练模型 71 | print("[INFO] training network...") 72 | opt = Adam(lr=1e-3, decay=1e-3 / 50) 73 | model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=["accuracy"]) 74 | H = model.fit(trainX, trainY, validation_data=(testX, testY), 75 | epochs=50, batch_size=32) 76 | 77 | # 预测 78 | print("[INFO] evaluating network...") 79 | predictions = model.predict(testX, batch_size=32) 80 | print(classification_report(testY.argmax(axis=1), 81 | predictions.argmax(axis=1), target_names=lb.classes_)) 82 | -------------------------------------------------------------------------------- /Tutorials/machineLearning_Python/classify_images.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | @Time : 2019/3/16 12:04 5 | @Author : cai 6 | 7 | 教程来自 https://www.pyimagesearch.com/2019/01/14/machine-learning-in-python/ 8 | """ 9 | from sklearn.neighbors import KNeighborsClassifier 10 | from sklearn.naive_bayes import GaussianNB 11 | from sklearn.linear_model import LogisticRegression 12 | from sklearn.svm import SVC 13 | from sklearn.tree import DecisionTreeClassifier 14 | from sklearn.ensemble import RandomForestClassifier 15 | from sklearn.neural_network import MLPClassifier 16 | from sklearn.preprocessing import LabelEncoder 17 | from sklearn.model_selection import train_test_split 18 | from sklearn.metrics import classification_report 19 | from PIL import Image 20 | from imutils import paths 21 | import numpy as np 22 | import argparse 23 | import os 24 | 25 | 26 | def extract_color_stats(image): 27 | ''' 28 | 将图片分成 RGB 三通道,然后分别计算每个通道的均值和标准差,然后返回 29 | :param image: 30 | :return: 31 | ''' 32 | (R, G, B) = image.split() 33 | features = [np.mean(R), np.mean(G), np.mean(B), np.std(R), np.std(G), np.std(B)] 34 | 35 | return features 36 | 37 | 38 | # 设置参数 39 | ap = argparse.ArgumentParser() 40 | ap.add_argument("-d", "--dataset", type=str, default="3scenes", 41 | help="path to directory containing the '3scenes' dataset") 42 | ap.add_argument("-m", "--model", type=str, default="knn", 43 | help="type of python machine learning model to use") 44 | args = vars(ap.parse_args()) 45 | 46 | # 定义一个保存模型的字典,根据 key 来选择加载哪个模型 47 | models = { 48 | "knn": KNeighborsClassifier(n_neighbors=1), 49 | "naive_bayes": GaussianNB(), 50 | "logit": LogisticRegression(solver="lbfgs", multi_class="auto"), 51 | "svm": SVC(kernel="rbf", gamma="auto"), 52 | "decision_tree": DecisionTreeClassifier(), 53 | "random_forest": RandomForestClassifier(n_estimators=100), 54 | "mlp": MLPClassifier() 55 | } 56 | # 加载数据并提取特征 57 | print("[INFO] extracting image features...") 58 | imagePaths = paths.list_images(args['dataset']) 59 | data = [] 60 | labels = [] 61 | 62 | # 循环遍历所有的图片数据 63 | for imagePath in imagePaths: 64 | # 加载图片,然后计算图片的颜色通道统计信息 65 | image = Image.open(imagePath) 66 | features = extract_color_stats(image) 67 | data.append(features) 68 | 69 | # 保存图片的标签信息 70 | label = imagePath.split(os.path.sep)[-2] 71 | labels.append(label) 72 | 73 | # 对标签进行编码,从字符串变为整数类型 74 | le = LabelEncoder() 75 | labels = le.fit_transform(labels) 76 | 77 | # 进行训练集和测试集的划分,75%数据作为训练集,其余25%作为测试集 78 | (trainX, testX, trainY, testY) = train_test_split(data, labels, random_state=3, test_size=0.25) 79 | # print('trainX numbers={}, testX numbers={}'.format(len(trainX), len(testX))) 80 | 81 | # 训练模型 82 | print("[INFO] using '{}' model".format(args["model"])) 83 | model = models[args["model"]] 84 | model.fit(trainX, trainY) 85 | 86 | # 预测并输出分类结果报告 87 | print("[INFO] evaluating...") 88 | predictions = model.predict(testX) 89 | print(classification_report(testY, predictions, target_names=le.classes_)) 90 | 91 | -------------------------------------------------------------------------------- /Tutorials/machineLearning_Python/classify_irs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | @Time : 2019/3/16 12:04 5 | @Author : cai 6 | 7 | 教程来自 https://www.pyimagesearch.com/2019/01/14/machine-learning-in-python/ 8 | """ 9 | from sklearn.neighbors import KNeighborsClassifier 10 | from sklearn.naive_bayes import GaussianNB 11 | from sklearn.linear_model import LogisticRegression 12 | from sklearn.svm import SVC 13 | from sklearn.tree import DecisionTreeClassifier 14 | from sklearn.ensemble import RandomForestClassifier 15 | from sklearn.neural_network import MLPClassifier 16 | from sklearn.model_selection import train_test_split 17 | from sklearn.metrics import classification_report 18 | from sklearn.datasets import load_iris 19 | import argparse 20 | 21 | # 设置参数 22 | ap = argparse.ArgumentParser() 23 | ap.add_argument("-m", "--model", type=str, default="knn", help="type of python machine learning model to use") 24 | args = vars(ap.parse_args()) 25 | 26 | # 定义一个保存模型的字典,根据 key 来选择加载哪个模型 27 | models = { 28 | "knn": KNeighborsClassifier(n_neighbors=1), 29 | "naive_bayes": GaussianNB(), 30 | "logit": LogisticRegression(solver="lbfgs", multi_class="auto"), 31 | "svm": SVC(kernel="rbf", gamma="auto"), 32 | "decision_tree": DecisionTreeClassifier(), 33 | "random_forest": RandomForestClassifier(n_estimators=100), 34 | "mlp": MLPClassifier() 35 | } 36 | 37 | # 载入 Iris 数据集,然后进行训练集和测试集的划分,75%数据作为训练集,其余25%作为测试集 38 | print("[INFO] loading data...") 39 | dataset = load_iris() 40 | (trainX, testX, trainY, testY) = train_test_split(dataset.data, dataset.target, random_state=3, test_size=0.25) 41 | 42 | # 训练模型 43 | print("[INFO] using '{}' model".format(args["model"])) 44 | model = models[args["model"]] 45 | model.fit(trainX, trainY) 46 | 47 | # 预测并输出一份分类结果报告 48 | print("[INFO] evaluating") 49 | predictions = model.predict(testX) 50 | print(classification_report(testY, predictions, target_names=dataset.target_names)) 51 | 52 | -------------------------------------------------------------------------------- /Tutorials/machineLearning_Python/nn_iris.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | """ 4 | @Time : 2019/3/16 12:05 5 | @Author : cai 6 | 7 | 教程来自 https://www.pyimagesearch.com/2019/01/14/machine-learning-in-python/ 8 | """ 9 | from keras.models import Sequential 10 | from keras.layers.core import Dense 11 | from keras.optimizers import SGD 12 | from sklearn.preprocessing import LabelBinarizer 13 | from sklearn.model_selection import train_test_split 14 | from sklearn.metrics import classification_report 15 | from sklearn.datasets import load_iris 16 | 17 | # 载入 Iris 数据集,然后进行训练集和测试集的划分,75%数据作为训练集,其余25%作为测试集 18 | print("[INFO] loading data...") 19 | dataset = load_iris() 20 | (trainX, testX, trainY, testY) = train_test_split(dataset.data, 21 | dataset.target, test_size=0.25) 22 | 23 | # 将标签进行 one-hot 编码 24 | lb = LabelBinarizer() 25 | trainY = lb.fit_transform(trainY) 26 | testY = lb.transform(testY) 27 | 28 | # 利用 Keras 定义网络模型 29 | model = Sequential() 30 | model.add(Dense(3, input_shape=(4,), activation="sigmoid")) 31 | model.add(Dense(3, activation="sigmoid")) 32 | model.add(Dense(3, activation="softmax")) 33 | 34 | # 采用梯度下降训练模型 35 | print('[INFO] training network...') 36 | opt = SGD(lr=0.1, momentum=0.9, decay=0.1 / 250) 37 | model.compile(loss='categorical_crossentropy', optimizer=opt, metrics=["accuracy"]) 38 | H = model.fit(trainX, trainY, validation_data=(testX, testY), epochs=250, batch_size=16) 39 | 40 | # 预测 41 | print('[INFO] evaluating network...') 42 | predictions = model.predict(testX, batch_size=16) 43 | print(classification_report(testY.argmax(axis=1), 44 | predictions.argmax(axis=1), target_names=dataset.target_names)) 45 | -------------------------------------------------------------------------------- /images/SSD_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccc013/DeepLearning_Notes/040201fb1654b01defdaf71db15f5a9bc1ca66fb/images/SSD_8.png -------------------------------------------------------------------------------- /images/SSD_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ccc013/DeepLearning_Notes/040201fb1654b01defdaf71db15f5a9bc1ca66fb/images/SSD_9.png -------------------------------------------------------------------------------- /编程基础/编程相关教程&资源&工具&博客网站.md: -------------------------------------------------------------------------------- 1 | # 教程 2 | - [GitHub五万星中文资源:命令行技巧大合集,新老司机各取所需](https://mp.weixin.qq.com/s/0cuaCaJNOXDxl4Kj90iuFg),Github:[https://github.com/jlevy/the-art-of-command-line/blob/master/README-zh.md](https://github.com/jlevy/the-art-of-command-line/blob/master/README-zh.md) 3 | - [听说 90% 的人都在 win10 下使用 ubuntu 子系统了!](https://mp.weixin.qq.com/s/16ryC-Os9ub828mbVS94Gg) 4 | - [CyC2018/CS-Notes](https://github.com/CyC2018/CS-Notes):技术基础,包含计算机基础、SQL、leetcode 刷题、工具等,在线阅读:[http://www.cyc2018.xyz/](http://www.cyc2018.xyz/) 5 | 6 | ​ 7 | 8 | ## Shell 9 | 10 | 11 | 文章: 12 | 13 | - [欲学机器学习必先掌握Shell,AI工程师自制教程,获Reddit网友400+点赞 | PDF+视频](https://mp.weixin.qq.com/s/gV0G6jub3U8GcUhnBrvu1g) 14 | - bash教程: [https://github.com/wangdoc/bash-tutorial,https://wangdoc.com/bash/intro.html](https://github.com/wangdoc/bash-tutorial,https://wangdoc.com/bash/intro.html) 15 | 16 | ​ 17 | 18 | Shell速查表:https://en.wikipedia.org/wiki/Samsung_Galaxy_Fold 19 | 视频教程地址:https://www.youtube.com/playlist?list=PLdfA2CrAqQ5kB8iSbm5FB1ADVdBeOzVqZ 20 | Shell脚本在线速查表:https://devhints.io/bash 21 | Bash教程电子书:https://books.goalkicker.com/BashBook/ 22 | 博客——从头开始学习Bash中的数据分析:https://data36.com/learn-data-analytics-bash-scratch/ 23 | 24 | 25 | ## Vim 26 | 27 | - [Vim 从入门到精通](https://github.com/wsdjeg/vim-galore-zh_cn) 28 | - [学会这21条,你离 Vim 大神就不远了!](https://mp.weixin.qq.com/s/y4BvS4rN3iVvXcupZVLrew) 29 | ## Git 30 | 31 | - Learning Git Branching:[https://learngitbranching.js.org](https://learngitbranching.js.org),[github](https://github.com/pcottle/learnGitBranching) 32 | - [猴子都懂的 Git 入门](https://backlog.com/git-tutorial/cn/intro/intro1_1.html) 33 | - [Git Magic](http://www-cs-students.stanford.edu/~blynn/gitmagic/intl/zh_cn/) 34 | - [廖雪峰的 Git 教程](https://www.liaoxuefeng.com/wiki/896043488029600/896067008724000) 35 | 36 | 37 | 38 | ## SQL 39 | 40 | - [SQLZOO](https://sqlzoo.net/):练习网站 41 | 42 | 43 | 44 | 45 | --- 46 | 47 | 48 | 49 | # 网站 50 | ## 阅读平台 51 | 52 | - [medium](https://medium.com/) 53 | - [Towards Data Science](https://towardsdatascience.com/) 54 | - [quora](https://www.quora.com/) 55 | - [stackoverflow](https://stackoverflow.com/) 56 | - [开发者头条](https://toutiao.io/) 57 | - [掘金](https://juejin.im/timeline) 58 | - [segmentfault](https://segmentfault.com/) 59 | 60 | 61 | 62 | ## 课程学习 63 | 64 | - [网易云课堂](https://study.163.com/) 65 | - [慕课网](http://www.imooc.com/course/list) 66 | - [牛客网](https://www.nowcoder.com/) 67 | - [SQL学习网站](https://sqlzoo.net/) 68 | - [有了这 15 款编程游戏,谁都可以学编程!](https://mp.weixin.qq.com/s/lM3zzgmgIlDNvmd3lhfhAA) 69 | 70 | 71 | 72 | ## 工具网站 73 | 74 | - [ReadMoreJoy](https://search.readmorejoy.com/)--搜索网站,包括百度、Bing、Google、知乎、微信和Github 75 | - [Codelf](https://unbug.github.io/codelf/)--给程序变量起名 76 | - [在线工具--程序员的工具箱](https://tool.lu/) 77 | - [MikuTools--一个轻量级的工具箱](https://miku.tools/) 78 | - [国外视频网站下载工具](https://en.savefrom.net/) 79 | - [字幕下载](http://downsub.com/)--支持YouTube、谷歌云盘、facebook 80 | - [https://carbon.now.sh/](https://carbon.now.sh/)--将代码转换成图片 81 | - [http://wangchujiang.com/awesome-mac/index.zh.html](http://wangchujiang.com/awesome-mac/index.zh.html):mac 软件下载 82 | 83 | 84 | 85 | ## 论坛 86 | 87 | - [V2EX](https://www.v2ex.com/)--一个科技宅/程序员的新奇想法聚集地,里面分了很多个讨论块,有技术、创意、好玩、Apple、酷工作、交易、城市、问与答、最热、全部、R2这十一个大模块 88 | 89 | 90 | 91 | ## 图片 92 | 93 | 94 | - [Unsplash](https://unsplash.com/)--真实的摄影照片,分辨率也很大,一张图差不多5M,每天都有内容更新 95 | - [Pexels](https://www.pexels.com/)--图片全部是用户上传的,每张图明确标记了是否可以免费使用,同时每张图都有详细信息,包括拍摄的相机型号、光圈、焦距、分辨率等等 96 | - [pixabay](https://pixabay.com/)--这个网站的图片也是免费的,不管你是不是商业用途都免费,同时还配有APP,可以收藏一下。 97 | 98 | --- 99 | 100 | 101 | 102 | # 博客 103 | 104 | 105 | 106 | 107 | 108 | --- 109 | 110 | # 电脑工具推荐 111 | 112 | - [编程神器 Mac 实用配置](https://mp.weixin.qq.com/s/BVfqf_xFN7TqYdpmwzXLKw) 113 | - [我的Win实用软件清单](https://mp.weixin.qq.com/s/VW5vSnXjGiaylS5027oHPA) 114 | 115 | ​ 116 | 117 | ## Mac 工具 118 | 119 | - youtube-dl 120 | 121 | 122 | 123 | ## 浏览器插件 124 | ### 下载工具 125 | 126 | - downie:[https://www.cndownie.com/faq-199.html](https://www.cndownie.com/faq-199.html) 127 | 128 | 129 | 130 | 131 | 132 | ### 写作相关 133 | 134 | - 文章同步助手:[https://github.com/wechatsync/Wechatsync](https://github.com/wechatsync/Wechatsync) 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | --- 144 | 145 | 146 | 147 | # Github 工具 148 | 149 | - 文件加密分享神器--[https://github.com/mozilla/send](https://github.com/mozilla/send) 150 | - GitHub 通知客户端--[https://github.com/devhubapp/devhub](https://github.com/devhubapp/devhub),跨平台,PC、移动端的 Android 和 IOS 都有 151 | - 下载工具 Motrix--[https://github.com/agalwood/Motrix](https://github.com/agalwood/Motrix) 152 | - 视频编辑神器:olive--[https://github.com/olive-editor/olive](https://github.com/olive-editor/olive) 153 | - 在线像素风格生成工具 Pixel Art to CSS--[https://github.com/jvalen/pixel-art-react/](https://github.com/jvalen/pixel-art-react/) 154 | - [在线制作`sorry 为所欲为`表情包的gif ](https://github.com/xtyxtyx/sorry) 155 | - [ ] [命令行番茄时钟](https://github.com/JaDogg/pydoro) 156 | --------------------------------------------------------------------------------