Faster R-CNN源碼解析(一)

目錄

    • 前言
    • 訓練腳本(train_mobilenetv2.py)
    • 自定義數據集(my_dataset.py)

前言

Faster R-CNN 是經典的two-stage目標檢測模型, 原理上并不是很復雜,也就是RPN+Fast R-CNN,但是在代碼的實現上確實有很多細節,并且源碼也非常的多,所以看源碼的時候也會遇到某些問題,網上完完整整從頭到尾講解的也很少,下面我將會為小伙伴們講解嗶哩嗶哩上一個up主簡單修改后的fast r-cnn源碼,大家可以去看他的視頻(源碼解析),原理以及源代碼真的非常詳細易懂,大家也可以去學習,爆贊!!我主要是記錄有些自己認為不太明白up主又沒有細講的部分(本人比較笨哈哈)以及梳理整個代碼流程,由于很多源碼還有debug的圖片我都寫在博客里,可能看起來會比較多,但是大家一定盡量看看,真的保姆級講解,大家跟著我走一定能弄懂的!!
下面奉上源碼鏈接(faster r-cnn源碼)

訓練腳本(train_mobilenetv2.py)

大家看源碼之前,可以先看文件里面的README.md文件,看一下需要的配置環境以及如何使用有些指令
源碼有兩個三個訓練腳本,我們不用管多GPU訓練的腳本,以train_mobilenetv2.py為例,這個是以mobilenetv2為backbone,另一個是resnet50+FPN(特征金字塔)為backbone的腳本,大差不大,只是結構不同,參數量不同,話不多說,我們直接看main()函數

def main():device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")print("Using {} device training.".format(device.type))# 用來保存coco_info的文件results_file = "results{}.txt".format(datetime.datetime.now().strftime("%Y%m%d-%H%M%S"))# 檢查保存權重文件夾是否存在,不存在則創建if not os.path.exists("save_weights"):os.makedirs("save_weights")data_transform = {"train": transforms.Compose([transforms.ToTensor(),transforms.RandomHorizontalFlip(0.5)]),"val": transforms.Compose([transforms.ToTensor()])}VOC_root = r"F:\AI\deep-learning-for-image-processing-master\pytorch_object_detection\faster_rcnn"  # VOCdevkitaspect_ratio_group_factor = 3batch_size = 8# check voc rootif os.path.exists(os.path.join(VOC_root, "VOCdevkit")) is False:raise FileNotFoundError("VOCdevkit dose not in path:'{}'.".format(VOC_root))# load train data set# VOCdevkit -> VOC2012 -> ImageSets -> Main -> train.txttrain_dataset = VOCDataSet(VOC_root, "2012", data_transform["train"], "train.txt")train_sampler = None# 是否按圖片相似高寬比采樣圖片組成batch# 使用的話能夠減小訓練時所需GPU顯存,默認使用if aspect_ratio_group_factor >= 0:train_sampler = torch.utils.data.RandomSampler(train_dataset)# 統計所有圖像高寬比例在bins區間中的位置索引group_ids = create_aspect_ratio_groups(train_dataset, k=aspect_ratio_group_factor)# 每個batch圖片從同一高寬比例區間中取train_batch_sampler = GroupedBatchSampler(train_sampler, group_ids, batch_size)nw = min([os.cpu_count(), batch_size if batch_size > 1 else 0, 8])  # number of workers# nw = 0print('Using %g dataloader workers' % nw)# 注意這里的collate_fn是自定義的,因為讀取的數據包括image和targets,不能直接使用默認的方法合成batchif train_sampler:# 如果按照圖片高寬比采樣圖片,dataloader中需要使用batch_samplertrain_data_loader = torch.utils.data.DataLoader(train_dataset,batch_sampler=train_batch_sampler,pin_memory=True,num_workers=nw,collate_fn=train_dataset.collate_fn)else:train_data_loader = torch.utils.data.DataLoader(train_dataset,batch_size=batch_size,shuffle=True,pin_memory=True,num_workers=nw,collate_fn=train_dataset.collate_fn)# load validation data set# VOCdevkit -> VOC2012 -> ImageSets -> Main -> val.txtval_dataset = VOCDataSet(VOC_root, "2012", data_transform["val"], "val.txt")val_data_loader = torch.utils.data.DataLoader(val_dataset,batch_size=1,shuffle=False,pin_memory=True,num_workers=nw,collate_fn=val_dataset.collate_fn)# create model num_classes equal background + 20 classesmodel = create_model(num_classes=21)# print(model)model.to(device)train_loss = []learning_rate = []val_map = []# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ##  first frozen backbone and train 5 epochs                   ##  首先凍結前置特征提取網絡權重(backbone),訓練rpn以及最終預測網絡部分 ## # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #for param in model.backbone.parameters():param.requires_grad = False# define optimizerparams = [p for p in model.parameters() if p.requires_grad]optimizer = torch.optim.SGD(params, lr=0.005,momentum=0.9, weight_decay=0.0005)init_epochs = 5for epoch in range(init_epochs):# train for one epoch, printing every 10 iterationsmean_loss, lr = utils.train_one_epoch(model, optimizer, train_data_loader,device, epoch, print_freq=50, warmup=True)train_loss.append(mean_loss.item())learning_rate.append(lr)# evaluate on the test datasetcoco_info = utils.evaluate(model, val_data_loader, device=device)# write into txtwith open(results_file, "a") as f:# 寫入的數據包括coco指標還有loss和learning rateresult_info = [str(round(i, 4)) for i in coco_info + [mean_loss.item()]] + [str(round(lr, 6))]txt = "epoch:{} {}".format(epoch, '  '.join(result_info))f.write(txt + "\n")val_map.append(coco_info[1])  # pascal mAPtorch.save(model.state_dict(), "./save_weights/pretrain.pth")# # # # # # # # # # # # # # # # # # # # # # # # # # # ##  second unfrozen backbone and train all network     ##  解凍前置特征提取網絡權重(backbone),接著訓練整個網絡權重  ## # # # # # # # # # # # # # # # # # # # # # # # # # # ## 凍結backbone部分底層權重for name, parameter in model.backbone.named_parameters():split_name = name.split(".")[0]if split_name in ["0", "1", "2", "3"]:parameter.requires_grad = Falseelse:parameter.requires_grad = True# define optimizerparams = [p for p in model.parameters() if p.requires_grad]optimizer = torch.optim.SGD(params, lr=0.005,momentum=0.9, weight_decay=0.0005)# learning rate schedulerlr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer,step_size=3,gamma=0.33)num_epochs = 20for epoch in range(init_epochs, num_epochs+init_epochs, 1):# train for one epoch, printing every 50 iterationsmean_loss, lr = utils.train_one_epoch(model, optimizer, train_data_loader,device, epoch, print_freq=50, warmup=True)train_loss.append(mean_loss.item())learning_rate.append(lr)# update the learning ratelr_scheduler.step()# evaluate on the test datasetcoco_info = utils.evaluate(model, val_data_loader, device=device)# write into txtwith open(results_file, "a") as f:# 寫入的數據包括coco指標還有loss和learning rateresult_info = [str(round(i, 4)) for i in coco_info + [mean_loss.item()]] + [str(round(lr, 6))]txt = "epoch:{} {}".format(epoch, '  '.join(result_info))f.write(txt + "\n")val_map.append(coco_info[1])  # pascal mAP# save weights# 僅保存最后5個epoch的權重if epoch in range(num_epochs+init_epochs)[-5:]:save_files = {'model': model.state_dict(),'optimizer': optimizer.state_dict(),'lr_scheduler': lr_scheduler.state_dict(),'epoch': epoch}torch.save(save_files, "./save_weights/mobile-model-{}.pth".format(epoch))# plot loss and lr curveif len(train_loss) != 0 and len(learning_rate) != 0:from plot_curve import plot_loss_and_lrplot_loss_and_lr(train_loss, learning_rate)# plot mAP curveif len(val_map) != 0:from plot_curve import plot_mapplot_map(val_map)

代碼可能看起來會有點多,數據集用的是PASCAL VOC2012,了解數據集的目錄和結構之后,第一個要講解的是圖像增強部分(data_transform),這部分的圖像增強方法是自己封裝的,我們知道目標檢測之前會事先把數據集的每一張圖片的ground truth(真實框)通過人工標注出來,但是RandomHorizontalFlip(隨機水平翻轉)這個操作的時候,對應的真實框(左上x,左上y,右下x,右下y)的坐標也會變,所以需要自己封裝進行處理在這里插入圖片描述

class RandomHorizontalFlip(object):"""隨機水平翻轉圖像以及bboxes"""def __init__(self, prob=0.5):self.prob = probdef __call__(self, image, target):if random.random() < self.prob:height, width = image.shape[-2:]image = image.flip(-1)  # 水平翻轉圖片bbox = target["boxes"]# bbox: xmin, ymin, xmax, ymaxbbox[:, [0, 2]] = width - bbox[:, [2, 0]]  # 翻轉對應bbox坐標信息target["boxes"] = bboxreturn image, target

prob就是隨機水平翻轉的概率,bbox就是保存的所有真實框的坐標信息,一張圖片可能不止一個目標,所以bbox應該是[N, 4]的形狀, 通過bbox[:, [0, 2]] = width - bbox[:, [2, 0]]就實現了圖像翻轉的同時坐標也改變了,注意:上圖右邊翻轉后的坐標位置應該還是對應左上和右下,圖上標的右上和左下是不對的,其他的圖像增強方式都很簡單,看看就明白了,關于自定義數據集VOCDataSet類,后面會單獨將,將數據集加載到設備內存后,DataLoader中有個collate_fn參數,這是一個將圖片打包的操作,很簡單:

    def collate_fn(batch):return tuple(zip(*batch))

比如說我們一個batch設置8張圖片,debug到這個地方可以看看在這里插入圖片描述
這就是傳入的八張圖片,*batch代表取列表里面的所有元素,zip進行打包并轉成元組,加載數據后需要實例化模型,Pascal VOC2012只有20個類別,傳入21是因為還多了一個背景

def create_model(num_classes):# https://download.pytorch.org/models/vgg16-397923af.pth# 如果使用vgg16的話就下載對應預訓練權重并取消下面注釋,接著把mobilenetv2模型對應的兩行代碼注釋掉# vgg_feature = vgg(model_name="vgg16", weights_path="./backbone/vgg16.pth").features# backbone = torch.nn.Sequential(*list(vgg_feature._modules.values())[:-1])  # 刪除features中最后一個Maxpool層# backbone.out_channels = 512# https://download.pytorch.org/models/mobilenet_v2-b0353104.pthbackbone = MobileNetV2(weights_path="./backbone/mobilenet_v2.pth").featuresbackbone.out_channels = 1280  # 設置對應backbone輸出特征矩陣的channelsanchor_generator = AnchorsGenerator(sizes=((32, 64, 128, 256, 512),),aspect_ratios=((0.5, 1.0, 2.0),))roi_pooler = torchvision.ops.MultiScaleRoIAlign(featmap_names=['0'],  # 在哪些特征層上進行roi poolingoutput_size=[7, 7],   # roi_pooling輸出特征矩陣尺寸sampling_ratio=2)  # 采樣率model = FasterRCNN(backbone=backbone,num_classes=num_classes,rpn_anchor_generator=anchor_generator,box_roi_pool=roi_pooler)return model

將所有寫好的類都在這里實例化,MobileNetV2可以看這個(MobileNetV2),AnchorsGenerator后面會詳細講,MultiScaleRoIAlign是torchvision封裝的類,傳入特征層的名字,MobileNetV2特征提取后只會產生一個特征層,名字也是自己取的,所以傳入了一個值,output_size就是通過roipooling(Region Of Interest)后輸出的特征矩陣尺寸,最后組成FasterRCNN。

這里的訓練方式和以往訓練圖像分類模型的方式有所不同,這里首先凍結前置特征提取網絡權重(backbone),訓練rpn以及最終預測網絡部分,然后解凍前置特征提取網絡權重(backbone),接著訓練整個網絡權重,將訓練后的各個指標寫入到文件中,以及最終保存權重和畫圖。大致的話訓練流程大家應該都了解了吧,后面再細講訓練時封裝的方法,大家先有個體系結構,知道整個大概流程。

自定義數據集(my_dataset.py)

在這一部分開始之前,小伙伴們需要先了解PASCAL VOC2012數據集噢(必須要了解,不然后面看不懂),了解數據集后,我們清楚了數據集的目錄結構,再來看這部分代碼

class VOCDataSet(Dataset):"""讀取解析PASCAL VOC2007/2012數據集"""def __init__(self, voc_root, year="2012", transforms=None, txt_name: str = "train.txt"):assert year in ["2007", "2012"], "year must be in ['2007', '2012']"self.root = os.path.join(voc_root, "VOCdevkit", f"VOC{year}")self.img_root = os.path.join(self.root, "JPEGImages")self.annotations_root = os.path.join(self.root, "Annotations")# read train.txt or val.txt filetxt_path = os.path.join(self.root, "ImageSets", "Main", txt_name)assert os.path.exists(txt_path), "not found {} file.".format(txt_name)with open(txt_path) as read:self.xml_list = [os.path.join(self.annotations_root, line.strip() + ".xml")for line in read.readlines() if len(line.strip()) > 0]# check fileassert len(self.xml_list) > 0, "in '{}' file does not find any information.".format(txt_path)for xml_path in self.xml_list:assert os.path.exists(xml_path), "not found '{}' file.".format(xml_path)# read class_indictjson_file = './pascal_voc_classes.json'assert os.path.exists(json_file), "{} file not exist.".format(json_file)json_file = open(json_file, 'r')self.class_dict = json.load(json_file)json_file.close()self.transforms = transforms

傳入的參數
voc_root:數據集根目錄
year: 哪一年的Pascal VOC數據集
transform:圖像增強
txt_name: 根據訓練需求傳入VOCdevkit//VOC2012//ImageSets//Main文件夾中的txt文件
前面幾行os.path.join()代碼都是拼接需要的文件路徑,方便后面使用,這里我們以train.txt為例,拿到訓練集的txt文件,里面是所有訓練集圖片的編號,讀取里面的編號并拼接為xml后綴的形式,方便在Annotations文件夾中打開,拼接完后我們debug發現txt文件的編號對應我們最后得到的xml路徑下的編號在這里插入圖片描述在這里插入圖片描述

一直到這部分:

 json_file = './pascal_voc_classes.json'assert os.path.exists(json_file), "{} file not exist.".format(json_file)json_file = open(json_file, 'r')self.class_dict = json.load(json_file)json_file.close()

pascal_voc_classes.json這個json文件就是以字典的形式保存了Pascal VOC2012數據集的20個類別并用索引表示:在這里插入圖片描述
打開這個文件并讀取賦值給類變量(加載后記得close關閉文件),后面會用到。

在這里插入圖片描述

注意:上面是官方的說明,因為是自定義的數據集方法,繼承來自pytorch的DataSet類,要求所有的子類必須繼承__len__和__getitem__方法,所以這里是必不可少的,__len__就是返回當前數據集的長度,__getitem__用于返回處理后的圖片以及標簽,下面再細講。

def __getitem__(self, idx):# read xmlxml_path = self.xml_list[idx]with open(xml_path) as fid:xml_str = fid.read()xml = etree.fromstring(xml_str)data = self.parse_xml_to_dict(xml)["annotation"]img_path = os.path.join(self.img_root, data["filename"])image = Image.open(img_path)if image.format != "JPEG":raise ValueError("Image '{}' format not JPEG".format(img_path))boxes = []labels = []iscrowd = []assert "object" in data, "{} lack of object information.".format(xml_path)for obj in data["object"]:xmin = float(obj["bndbox"]["xmin"])xmax = float(obj["bndbox"]["xmax"])ymin = float(obj["bndbox"]["ymin"])ymax = float(obj["bndbox"]["ymax"])# 進一步檢查數據,有的標注信息中可能有w或h為0的情況,這樣的數據會導致計算回歸loss為nanif xmax <= xmin or ymax <= ymin:print("Warning: in '{}' xml, there are some bbox w/h <=0".format(xml_path))continueboxes.append([xmin, ymin, xmax, ymax])labels.append(self.class_dict[obj["name"]])if "difficult" in obj:iscrowd.append(int(obj["difficult"]))else:iscrowd.append(0)# convert everything into a torch.Tensorboxes = torch.as_tensor(boxes, dtype=torch.float32)labels = torch.as_tensor(labels, dtype=torch.int64)iscrowd = torch.as_tensor(iscrowd, dtype=torch.int64)image_id = torch.tensor([idx])area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0])target = {}target["boxes"] = boxestarget["labels"] = labelstarget["image_id"] = image_idtarget["area"] = areatarget["iscrowd"] = iscrowdif self.transforms is not None:image, target = self.transforms(image, target)return image, target

該方法通過索引參數 idx 載入每張圖片,因為類變量xml_list里面保存了所有的訓練集圖片的xml文件路徑,打開對應的xml文件,這里我debug得到的是這個xml文件(每次debug都可能不一樣,因為圖片是打亂了的)在這里插入圖片描述
讓我們再看看原圖片在這里插入圖片描述
xml文件里面標注的很清楚,三個真實框的坐標以及類別,我們通過lxml.etree.fromstring先展成數再換成string的格式,調用類方法parse_xml_to_dict(),傳入xml的內容,通過遞歸的方式循環遍歷每一層,得到最后的文件內容,
我們可以看看data = self.parse_xml_to_dict(xml)[“annotation”]的debug結果在這里插入圖片描述
這里我們也順利拿出了xml文件的所有內容,接下來打開圖片并判斷是不是JPEG格式的,不是就會報錯(原數據集都是jpeg的格式,報錯說明數據集有問題),再看上面解析xml后的內容,object是我們的目標,通過列表包裹了多個字典,通過循環列表以及字典的key拿到對應的value,后面一系列操作都是將需要的內容取出來用列表存儲,我解釋一下這幾個列表儲存的都是什么:
boxes 存儲的所有真實框的坐標
labels存儲的真實類別的索引,也就是上面加載的json文件中的類別索引
iscrowd儲存的是圖片是否難以檢測,0就是不難,非0就是比較難檢測

再將上述三個列表以及當前圖片的索引 idx 轉成tensor的形式,area是計算了所有真實框的面積,后面也會用到,最最最后在用一個字典target保存上述所有的信息,debug看一下結果:在這里插入圖片描述
最后圖像增強一下就好了,其他的部分都差不多,自己看看就好了,至此我們第一部分的代碼就講解完了

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/162987.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/162987.shtml
英文地址,請注明出處:http://en.pswp.cn/news/162987.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

chatglm3部署使用

chatglm3部署使用 1.部署2.使用3.接入微信4.vue前端 1.部署 1.首先去github下載chatglm3代碼。Huggingface下載模型一直失敗&#xff0c;所以用阿里的魔塔社區下載。 git clone https://github.com/THUDM/ChatGLM3.git git clone https://www.modelscope.cn/ZhipuAI/chatglm3…

docker常見問題匯總

docker常見問題 ?問題1&#xff1a;啟動docker容器時&#xff0c;報錯Unknown runtime specified nvidia. 當我啟動一個容器時&#xff0c;運行以下命令&#xff1a; docker run --runtimenvidia 。。。。 后面一部分命令沒寫出來&#xff0c;此時報錯的信息如下&#xff1a;…

python-opencv劃痕檢測

python-opencv劃痕檢測 這次實驗&#xff0c;我們將對如下圖片進行劃痕檢測&#xff0c;其實這個比較有難度&#xff0c;因為清晰度太差了。 我們做法如下&#xff1a; &#xff08;1&#xff09;讀取圖像為灰度圖像&#xff0c;進行自適應直方圖均衡化處理&#xff0c;增強圖…

thingsboard3.6的mailConfigTemplateController錯誤

1、bug內容 使用3.6版本的tb代碼進行打包生成boot的jar包,在啟動的時候會報錯mailConfigTemplateController bean初始化找不到文件路徑。 Error creating bean with name mailConfigTemplateController defined in URL [jar:file:/D:/yuxinwei/AE/thingsboard/thingsboard-3…

nuxt3項目修改端口號

nuxt的默認端口號是3000 一、修改開發環境端口號 方式一&#xff1a;使用環境變量配置,設置&#xff08;PORT 或 NUXT_PORT&#xff09; # .env PORT3001 #http://localhost:3001/ NITRO_PORT3001 #http://localhost:3001/ 方式二&#xff1a;nuxt.config.ts里配置…

vue2.0+elementui集成file-loader之后圖標失效問題

背景 跑vue2elementUI項目時&#xff0c;由于前端這邊需要在本地存放xlsx模板文件&#xff0c;供用戶下載模板文件&#xff0c;所以需要在webpack構建的時候增加file-loader進行解析xlsx文件打包。 vue版本2.x element-ui 版本 2.13.x 注意 npm i -D file-loader版本號給vue項…

操作系統 day12(調度算法的評價指標)

評價指標 CPU利用率 系統吞吐量 周轉時間 帶權周轉時間 等待時間 響應時間

vue中屬性的基本用法

v-for指令的用法 v-for指令用于重復輸出當前元素。 案例&#xff1a;寫一個新的組件頁面&#xff1a;For.vue 當訪問&#xff1a; /for時看到它。 場景1 遍歷字符串數組輸出每一個列表項元素&#xff1a; data: {nav: [京東超市,京東家電,秒殺,拍賣,京東生鮮,PLUS會員] } …

【python學習】中級篇-TCP編程Socket模塊:客戶端與服務端

客戶端 創建一個基于TCP連接的Socket AF_INET指定使用IPv4協議 AF_INET6指定使用IPv6協議 # 導入socket庫: import socket# 創建一個socket: s socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 建立連接: s.connect((www.sina.com.cn, 80))客戶端要主動發起TCP連接 必…

2022-1-25 機器人運動規劃方法綜述 航空學報

論文PDF abstract 隨著應用場景的日益復雜&#xff0c;機器人對旨在生成無碰撞路徑&#xff08;軌跡&#xff09;的自主運動規劃技術的需求也變得更加迫 切。雖然目前已產生了大量適應于不同場景的規劃算法&#xff0c;但如何妥善地對現有成果進行歸類&#xff0c;并分析不同…

Spark---基于Standalone模式提交任務

Standalone模式兩種提交任務方式 一、Standalone-client提交任務方式 1、提交命令 ./spark-submit --master spark://mynode1:7077 --class org.apache.spark.examples.SparkPi ../examples/jars/spark-examples_2.11-2.3.1.jar 100 或者 ./spark-submit --master spark…

給數據庫查詢結果添加一個額外的自增編號

1、在mysql數據庫可以執行的sql SELECT( i : i 1 ) num,M.* FROMuser M,( SELECT i : 0 ) AS ID GROUP BYM.ID ORDER BYM.create_time SELECT (i :i 1) 是為了生成自增的序列號字段 SELECT i : 0 是為了將i進行初始化每次查詢的序列號都會從1開始進行排序生成序列號 在…

go sync.map源碼解讀

此源碼理解僅為個人理解&#xff0c;如有錯誤歡迎指出 sync.map的數據結構主要包含四個字段 一個互斥鎖&#xff0c;readonly,dirty,和miss 從讀寫兩個方面來講這幾個變量 readonly其實就是有bool值的dirty&#xff0c;底層結構都是map readonly的讀更新不會上鎖&#xff0c…

在springboot中實現WebSocket協議通信

前面介紹了使用netty實現websocket通信&#xff0c;有些時候&#xff0c;如果我們的服務并不復雜或者連接數并不高&#xff0c;單獨搭建一個websocket服務端有些浪費資源&#xff0c;這時候我們就可以在web服務內提供簡單的websocket連接支持。其實springboot已經支持了websock…

20230511 Windows Ubuntu vscode remote-ssh 連接配置

參考 &#xff1a; VSCode SSH 連接遠程ubuntu Linux 主機 VSCode通過Remote SSH擴展連接到內網Ubuntu主機 Ubuntu 安裝 sudo apt-get install openssh-server vscode: 安裝remote-ssh 插件 連接到服務器IP 免密登錄的公鑰密鑰傳遞用filezillaUbuntu 和 Windows 文件互傳 …

PMP對項目工程師有用嗎?

一、什么是項目工程師&#xff1f; 項目工程師是指在各個領域負責技術操作、設計、管理以及評估能力的人員。他們通常擔當項目的實施和執行角色&#xff0c;在開發或控制類項目中發揮重要作用。有時&#xff0c;項目工程師的稱號還可以用來表示在某個領域取得專業資格的人員。…

深入理解路由協議:從概念到實踐

路由技術是Internet得以持續運轉的關鍵所在&#xff0c;路由是極其有趣而又復雜的課題&#xff0c;永遠的話題。 SO&#xff1a;這是一個解析路由協議的基礎文章。 目錄 前言路由的概念路由協議的分類數據包在網絡中的路由過程理解路由表的結構路由器關鍵功能解析 前言 在互聯…

PTA-字符串的連接

本題要求實現一個函數&#xff0c;將兩個字符串連接起來。 函數接口定義&#xff1a; char *str_cat( char *s, char *t ); 函數str_cat應將字符串t復制到字符串s的末端&#xff0c;并且返回字符串s的首地址。 裁判測試程序樣例&#xff1a; #include <stdio.h> #inc…

SQL Server數據庫自動備份方法

要編寫一個自動備份 SQL Server 數據庫的腳本&#xff0c;可以使用 SQL Server Management Studio (SSMS) 或者 Transact-SQL (T-SQL) 腳本。以下是一個簡單的 T-SQL 腳本示例&#xff0c;該腳本將數據庫備份到指定的文件路徑&#xff1a; -- 設置要備份的數據庫名稱 DECLARE D…

求解Beamforming-SOCP(CVX求解)

時間&#xff1a;2023年11月23日14:00:16&#xff1a; 直接上代碼&#xff08;辛苦兩天才改出來的&#xff09; clear all; K 4; %user number N4; %base station number var1e-9; H []; %initialize H matrix for i1:Kh 1/sqrt(2*K)*mvnrnd(zeros(N,1),eye(N),1)1i/sqrt(2*…