基于ResNet模型的908種超大規模中草藥圖像識別系統

中草藥藥材圖像識別相關的實踐在前文中已有對應的實踐了,感興趣的話可以自行移步閱讀即可:

《python基于輕量級GhostNet模型開發構建23種常見中草藥圖像識別系統》

《基于輕量級MnasNet模型開發構建40種常見中草藥圖像識別系統》

在上一篇文章中,我們提到在自主開發構建大規模的中草藥數據集,本文就是建立在這樣的背景基礎上的,目前已經構建了包含908種中草藥數據的基礎數據集,整體如下:

首先看下整體效果:

類別實例如下:

車前草
金銀花
蒲公英
鴨跖草
垂盆草
酸漿
蒼耳
馬蘭頭
薺菜
小薊
水芹菜
天胡荽
酢漿草
婆婆指甲菜
漆姑草
通泉草
波斯婆婆納
澤漆
狗尾巴草
旋復花
黃花菜
小飛蓬
金線草
鴨舌草
蘭花參
柴胡
麥冬
蛇莓
玉竹
桑白皮
曼陀羅
鬼針草
苦菜
葵菜
蕁麻
龍葵
蒺藜
何首烏
野薄荷
棕櫚
夏枯草
絞股藍
紫云英
七星草
芍藥
貝母
當歸
丹皮
柴胡
車前草
紫蘇
益母草
枇杷葉
荷葉
大青葉
艾葉
野菊花
金銀花
月季花
旋覆花
蓮子
菟絲子
銀杏
茴香
天麻
葛根
桔梗
黃柏
杜仲
厚樸
全蝎
地龍
土鱉蟲
蟋蟀
貝殼
珍珠
磁石
麻黃
桂枝
生姜
香薷
紫蘇葉
藁本
辛夷
防風
白芷
荊芥
羌活
蒼耳子
薄荷
牛蒡子
蔓荊子
蟬蛻
桑葉
葛根
柴胡
升麻
淡豆豉
知母
梔子
夏枯草
蘆根
天花粉
淡竹葉
黃芩
黃連
黃柏
龍膽
苦參
犀角
生地黃
玄參
牡丹皮
赤芍
金銀花
連翹
魚腥草
熟地黃
黨參
桂枝
山藥
枸杞子
車前草
紫蘇
大青葉
荷葉
青皮薄荷
柴胡
香附
當歸
黃芪
西洋參
茯苓
蒼術
艾葉
老姜
當歸
香附
益母草
玫瑰花
桑枝
薄荷
木瓜
雞血藤
女貞子
蓮子
薏米
百合
人參
太子參
鹿茸
龜板
鱉甲
杏仁
桔梗
陳皮
丹參
川芎
旱蓮草
車前子
大黃
夏枯草
連翹
金銀花
桂枝
柴胡
香附
薄荷
青皮
香櫞
佛手
熟地
當歸
川芎
白芍
阿膠
丹參
三七
桃仁
紅花
元胡
生地
石斛
沙參
麥冬
巴戟天
鎖陽
火炭母
地膽草
崩大碗
絞股藍
布荊
八角楓
八角茴香
八角金盤
八角蓮
八角蓮葉

目前數據總類別共有908種,來自我們不同成員的匯總,后續有新的類目可以持續進行擴充累積。

考慮到如此大類目的圖像識別,本文選擇的是經典的ResNet模型,殘差網絡(ResNet)是一種深度學習架構,用于解決深度神經網絡中的梯度消失和梯度爆炸問題。它引入了殘差塊(residual block)的概念,使網絡能夠更輕松地學習恒等映射,從而提高網絡的訓練效果。

ResNet的構建原理如下:

  1. 基礎模塊:ResNet的基礎模塊是殘差塊。每個殘差塊由兩個卷積層組成,每個卷積層后面跟著一個批量歸一化層(batch normalization)和一個激活函數(通常是ReLU)。這兩個卷積層的輸出通過跳躍連接(skip connection)相加,然后再通過激活函數。這個跳躍連接允許信息直接流過殘差塊,從而避免了信息在網絡中丟失或衰減。

  2. 堆疊殘差塊:ResNet通過堆疊多個殘差塊來構建更深的網絡。這些殘差塊可以有不同的層數和濾波器數量,以適應不同的任務和網絡深度需求。

  3. 池化層和全連接層:在堆疊殘差塊之后,可以添加池化層來減小特征圖的尺寸,并通過全連接層對最終的特征進行分類或回歸。

ResNet的優點:

  1. 解決梯度消失和梯度爆炸問題:由于殘差塊中的跳躍連接,ResNet可以更輕松地訓練深層網絡,避免了梯度在反向傳播過程中的消失或爆炸。

  2. 提高網絡的訓練效果:殘差塊允許網絡學習恒等映射,即將輸入直接傳遞到輸出。這使得網絡可以更容易地學習殘差部分,從而提高了網絡的訓練效果。

  3. 可以構建非常深的網絡:由于殘差連接的存在,ResNet可以堆疊更多的殘差塊,構建非常深的網絡。這有助于提取更復雜的特征,從而提高模型的表達能力。

ResNet的缺點:

  1. 參數較多:由于ResNet的深度,網絡中存在大量的參數,這會增加模型的復雜度和訓練時間。

  2. 訓練困難:盡管ResNet可以解決梯度消失和梯度爆炸問題,但在訓練較深的ResNet時,仍然可能出現其他訓練困難,如梯度退化問題和過擬合。

ResNet通過引入殘差塊和跳躍連接的方式,解決了深度神經網絡中的梯度消失和梯度爆炸問題,并提高了網絡的訓練效果。

這里給出對應的代碼實現:

# coding=utf-8
from keras.models import Model
from keras.layers import (Input,Dense,BatchNormalization,Conv2D,MaxPooling2D,AveragePooling2D,ZeroPadding2D,
)
from keras.layers import add, Flatten
from keras.optimizers import SGD
import numpy as npseed = 7
np.random.seed(seed)def Conv2d_BN(x, nb_filter, kernel_size, strides=(1, 1), padding="same", name=None):if name is not None:bn_name = name + "_bn"conv_name = name + "_conv"else:bn_name = Noneconv_name = Nonex = Conv2D(nb_filter,kernel_size,padding=padding,strides=strides,activation="relu",name=conv_name,)(x)x = BatchNormalization(axis=3, name=bn_name)(x)return xdef Conv_Block(inpt, nb_filter, kernel_size, strides=(1, 1), with_conv_shortcut=False):x = Conv2d_BN(inpt,nb_filter=nb_filter[0],kernel_size=(1, 1),strides=strides,padding="same",)x = Conv2d_BN(x, nb_filter=nb_filter[1], kernel_size=(3, 3), padding="same")x = Conv2d_BN(x, nb_filter=nb_filter[2], kernel_size=(1, 1), padding="same")if with_conv_shortcut:shortcut = Conv2d_BN(inpt, nb_filter=nb_filter[2], strides=strides, kernel_size=kernel_size)x = add([x, shortcut])return xelse:x = add([x, inpt])return xdef ResNet():inpt = Input(shape=(224, 224, 3))x = ZeroPadding2D((3, 3))(inpt)x = Conv2d_BN(x, nb_filter=64, kernel_size=(7, 7), strides=(2, 2), padding="valid")x = MaxPooling2D(pool_size=(3, 3), strides=(2, 2), padding="same")(x)x = Conv_Block(x,nb_filter=[64, 64, 256],kernel_size=(3, 3),strides=(1, 1),with_conv_shortcut=True,)x = Conv_Block(x, nb_filter=[64, 64, 256], kernel_size=(3, 3))x = Conv_Block(x, nb_filter=[64, 64, 256], kernel_size=(3, 3))x = Conv_Block(x,nb_filter=[128, 128, 512],kernel_size=(3, 3),strides=(2, 2),with_conv_shortcut=True,)x = Conv_Block(x, nb_filter=[128, 128, 512], kernel_size=(3, 3))x = Conv_Block(x, nb_filter=[128, 128, 512], kernel_size=(3, 3))x = Conv_Block(x, nb_filter=[128, 128, 512], kernel_size=(3, 3))x = Conv_Block(x,nb_filter=[256, 256, 1024],kernel_size=(3, 3),strides=(2, 2),with_conv_shortcut=True,)x = Conv_Block(x, nb_filter=[256, 256, 1024], kernel_size=(3, 3))x = Conv_Block(x, nb_filter=[256, 256, 1024], kernel_size=(3, 3))x = Conv_Block(x, nb_filter=[256, 256, 1024], kernel_size=(3, 3))x = Conv_Block(x, nb_filter=[256, 256, 1024], kernel_size=(3, 3))x = Conv_Block(x, nb_filter=[256, 256, 1024], kernel_size=(3, 3))x = Conv_Block(x,nb_filter=[512, 512, 2048],kernel_size=(3, 3),strides=(2, 2),with_conv_shortcut=True,)x = Conv_Block(x, nb_filter=[512, 512, 2048], kernel_size=(3, 3))x = Conv_Block(x, nb_filter=[512, 512, 2048], kernel_size=(3, 3))x = AveragePooling2D(pool_size=(7, 7))(x)x = Flatten()(x)x = Dense(908, activation="softmax")(x)model = Model(inputs=inpt, outputs=x)sgd = SGD(decay=0.0001, momentum=0.9)model.compile(loss="categorical_crossentropy", optimizer=sgd, metrics=["accuracy"])model.summary()

上面是基于Keras框架實現的,當然了也可以基于PyTorch框架實現,如下所示:

import torch
from torch import Tensor
import torch.nn as nn
import numpy as np
from torchvision._internally_replaced_utils import load_state_dict_from_url
from typing import Type, Any, Callable, Union, List, Optionaldef conv3x3(in_planes: int, out_planes: int, stride: int = 1, groups: int = 1, dilation: int = 1
) -> nn.Conv2d:return nn.Conv2d(in_planes,out_planes,kernel_size=3,stride=stride,padding=dilation,groups=groups,bias=False,dilation=dilation,)def conv1x1(in_planes: int, out_planes: int, stride: int = 1) -> nn.Conv2d:return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False)class BasicBlock(nn.Module):expansion: int = 1def __init__(self,inplanes: int,planes: int,stride: int = 1,downsample: Optional[nn.Module] = None,groups: int = 1,base_width: int = 64,dilation: int = 1,norm_layer: Optional[Callable[..., nn.Module]] = None,) -> None:super(BasicBlock, self).__init__()if norm_layer is None:norm_layer = nn.BatchNorm2dif groups != 1 or base_width != 64:raise ValueError("BasicBlock only supports groups=1 and base_width=64")if dilation > 1:raise NotImplementedError("Dilation > 1 not supported in BasicBlock")self.conv1 = conv3x3(inplanes, planes, stride)self.bn1 = norm_layer(planes)self.relu = nn.ReLU(inplace=True)self.conv2 = conv3x3(planes, planes)self.bn2 = norm_layer(planes)self.downsample = downsampleself.stride = stridedef forward(self, x: Tensor) -> Tensor:identity = xout = self.conv1(x)out = self.bn1(out)out = self.relu(out)out = self.conv2(out)out = self.bn2(out)if self.downsample is not None:identity = self.downsample(x)out += identityout = self.relu(out)return outclass Bottleneck(nn.Module):expansion: int = 4def __init__(self,inplanes: int,planes: int,stride: int = 1,downsample: Optional[nn.Module] = None,groups: int = 1,base_width: int = 64,dilation: int = 1,norm_layer: Optional[Callable[..., nn.Module]] = None,) -> None:super(Bottleneck, self).__init__()if norm_layer is None:norm_layer = nn.BatchNorm2dwidth = int(planes * (base_width / 64.0)) * groups# Both self.conv2 and self.downsample layers downsample the input when stride != 1self.conv1 = conv1x1(inplanes, width)self.bn1 = norm_layer(width)self.conv2 = conv3x3(width, width, stride, groups, dilation)self.bn2 = norm_layer(width)self.conv3 = conv1x1(width, planes * self.expansion)self.bn3 = norm_layer(planes * self.expansion)self.relu = nn.ReLU(inplace=True)self.downsample = downsampleself.stride = stridedef forward(self, x: Tensor) -> Tensor:identity = xout = self.conv1(x)out = self.bn1(out)out = self.relu(out)out = self.conv2(out)out = self.bn2(out)out = self.relu(out)out = self.conv3(out)out = self.bn3(out)if self.downsample is not None:identity = self.downsample(x)out += identityout = self.relu(out)return outclass ResNet(nn.Module):def __init__(self,block: Type[Union[BasicBlock, Bottleneck]],layers: List[int],num_classes: int = 1000,zero_init_residual: bool = False,groups: int = 1,width_per_group: int = 64,replace_stride_with_dilation: Optional[List[bool]] = None,norm_layer: Optional[Callable[..., nn.Module]] = None,) -> None:super(ResNet, self).__init__()if norm_layer is None:norm_layer = nn.BatchNorm2dself._norm_layer = norm_layerself.inplanes = 64self.dilation = 1if replace_stride_with_dilation is None:replace_stride_with_dilation = [False, False, False]if len(replace_stride_with_dilation) != 3:raise ValueError("replace_stride_with_dilation should be None ""or a 3-element tuple, got {}".format(replace_stride_with_dilation))self.groups = groupsself.base_width = width_per_groupself.conv1 = nn.Conv2d(3, self.inplanes, kernel_size=7, stride=2, padding=3, bias=False)self.bn1 = norm_layer(self.inplanes)self.relu = nn.ReLU(inplace=True)self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)self.layer1 = self._make_layer(block, 64, layers[0])self.layer2 = self._make_layer(block, 128, layers[1], stride=2, dilate=replace_stride_with_dilation[0])self.layer3 = self._make_layer(block, 256, layers[2], stride=2, dilate=replace_stride_with_dilation[1])self.layer4 = self._make_layer(block, 512, layers[3], stride=2, dilate=replace_stride_with_dilation[2])self.avgpool = nn.AdaptiveAvgPool2d((1, 1))self.fc = nn.Linear(512 * block.expansion, num_classes)for m in self.modules():if isinstance(m, nn.Conv2d):nn.init.kaiming_normal_(m.weight, mode="fan_out", nonlinearity="relu")elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):nn.init.constant_(m.weight, 1)nn.init.constant_(m.bias, 0)if zero_init_residual:for m in self.modules():if isinstance(m, Bottleneck):nn.init.constant_(m.bn3.weight, 0)  # type: ignore[arg-type]elif isinstance(m, BasicBlock):nn.init.constant_(m.bn2.weight, 0)  # type: ignore[arg-type]def _make_layer(self,block: Type[Union[BasicBlock, Bottleneck]],planes: int,blocks: int,stride: int = 1,dilate: bool = False,) -> nn.Sequential:norm_layer = self._norm_layerdownsample = Noneprevious_dilation = self.dilationif dilate:self.dilation *= stridestride = 1if stride != 1 or self.inplanes != planes * block.expansion:downsample = nn.Sequential(conv1x1(self.inplanes, planes * block.expansion, stride),norm_layer(planes * block.expansion),)layers = []layers.append(block(self.inplanes,planes,stride,downsample,self.groups,self.base_width,previous_dilation,norm_layer,))self.inplanes = planes * block.expansionfor _ in range(1, blocks):layers.append(block(self.inplanes,planes,groups=self.groups,base_width=self.base_width,dilation=self.dilation,norm_layer=norm_layer,))return nn.Sequential(*layers)def _forward_impl(self, x: Tensor, need_fea=False) -> Tensor:if need_fea:features, features_fc = self.forward_features(x, need_fea)x = self.fc(features_fc)return features, features_fc, xelse:x = self.forward_features(x)x = self.fc(x)return xdef forward(self, x: Tensor, need_fea=False) -> Tensor:return self._forward_impl(x, need_fea)def forward_features(self, x, need_fea=False):x = self.conv1(x)x = self.bn1(x)x = self.relu(x)x = self.maxpool(x)if need_fea:x1 = self.layer1(x)x2 = self.layer2(x1)x3 = self.layer3(x2)x4 = self.layer4(x3)x = self.avgpool(x4)x = torch.flatten(x, 1)return [x1, x2, x3, x4], xelse:x = self.layer1(x)x = self.layer2(x)x = self.layer3(x)x = self.layer4(x)x = self.avgpool(x)x = torch.flatten(x, 1)return xdef cam_layer(self):return self.layer4def _resnet(block: Type[Union[BasicBlock, Bottleneck]],layers: List[int],pretrained: bool,progress: bool,**kwargs: Any
) -> ResNet:model = ResNet(block, layers, **kwargs)if pretrained:state_dict = load_state_dict_from_url("https://download.pytorch.org/models/resnet50-0676ba61.pth",progress=progress,)model_dict = model.state_dict()weight_dict = {}for k, v in state_dict.items():if k in model_dict:if np.shape(model_dict[k]) == np.shape(v):weight_dict[k] = vpretrained_dict = weight_dictmodel_dict.update(pretrained_dict)model.load_state_dict(model_dict)return modeldef resnet50(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> ResNet:return _resnet(Bottleneck, [3, 4, 6, 3], pretrained, progress, **kwargs)

可以根據自己的喜好,直接集成到自己的項目中進行使用都是可以的。

整體訓練loss曲線如下所示:

準確率曲線如下所示:

目前僅僅從零開始訓練了60多個epoch,效果不是很理想,后續計劃基于預訓練的模型權重來進行微調訓練提升當前的精度。

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

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

相關文章

RocketMQ-RocketMQ高性能核心原理(流程圖)

1.NamesrvStartup 2.BrokerStartup 3. DefualtMQProducer 4.DefaultMQPushConsumer

maven工程的pom.xml文件中增加了依賴,但偶爾沒有下載到本地倉庫

maven工程pom.xml文件中的個別依賴沒有下載到本地maven倉庫。以前沒有遇到這種情況,今天就遇到了這個問題,把解決過程記錄下來。 我在eclipse中編輯maven工程的pom.xml文件,增加對mybatis的依賴,但保存文件后,依賴的j…

Java--1v1雙向通信-控制臺版

文章目錄 前言客戶端服務器端輸出線程端End 前言 TCP(Transmission Control Protocol)是一種面向連接的、可靠的網絡傳輸協議,它提供了端到端的數據傳輸和可靠性保證。 本程序就是基于tcp協議編寫而成的。 利用 TCP 協議進行通信的兩個應用…

HarmonyOS(鴻蒙操作系統)與Android系統 各自特點 架構對比 各自優勢

綜合對比 HarmonyOS(鴻蒙操作系統)是由華為開發的操作系統,旨在跨多種設備和平臺使用。HarmonyOS的架構與谷歌開發的廣泛使用的Android操作系統有顯著不同。以下是兩者之間的一些主要比較點: 設計理念和使用案例: Harm…

go語言 grpc 攔截器

文章目錄 攔截器服務端攔截器一元攔截器流攔截器 客戶端攔截器一元攔截器流攔截 多個攔截器 代碼倉庫 攔截器 gRPC攔截器(interceptor)是一種函數,它可以在gRPC調用之前和之后執行一些邏輯,例如認證、授權、日志記錄、監控和統計…

iOS app切換后臺時添加模糊遮罩層

仿 支付寶 退出后臺后,App整個 增加模糊遮罩層 此處只介紹 在iOS13后 SceneDelegate 下的操作 原理就是 在 App 進入后臺后 在 主window上添加一個 UIVisualEffectView 在進入前臺后移除 直接上代碼: 先聲明: //先聲明 /* blurView */ property (strong, nonatomic) UI…

逆波蘭表達式求解計算器

利用逆波蘭表達式求解計算器有以下幾個步驟: 1. 去掉字符串中的空格 s s.replaceAll(" ", "")2. 講字符串轉換為中序表達式數組 def string_to_infixlist(s):ans []keep_num ""for i in range(len(s)):if s[i].isdigit():if i < len(s)…

docker學習(四、修改容器創建新的鏡像推送到云上)

鏡像是只讀的&#xff0c;容器是可編輯的。Docker鏡像是分層的&#xff0c;支持通過擴展鏡像&#xff0c;創建新的鏡像。 學到這里感覺docker跟git很想~~ 通過docker commit將修改的容器做成新的鏡像 # 將容器做成新的鏡像 docker commit -m"提交備注" -a"作…

【1day】泛微e-office OA系統sms_page.php接口SQL 注入漏洞學習

注:該文章來自作者日常學習筆記,請勿利用文章內的相關技術從事非法測試,如因此產生的一切不良后果與作者無關。 目錄 一、漏洞描述 二、影響版本 三、資產測繪 四、漏洞復現

大創項目推薦 交通目標檢測-行人車輛檢測流量計數 - 大創項目推薦

文章目錄 0 前言1\. 目標檢測概況1.1 什么是目標檢測&#xff1f;1.2 發展階段 2\. 行人檢測2.1 行人檢測簡介2.2 行人檢測技術難點2.3 行人檢測實現效果2.4 關鍵代碼-訓練過程 最后 0 前言 &#x1f525; 優質競賽項目系列&#xff0c;今天要分享的是 &#x1f6a9; 畢業設計…

什么是Nginx反向代理?Nginx反向代理配置指南

Nginx反向代理是一種常見的服務器架構模式&#xff0c;它可以將客戶端請求轉發到多個后端服務器上&#xff0c;從而實現負載均衡、高可用性和安全性。本文將介紹Nginx反向代理的基本概念和配置方法。 什么是Nginx反向代理&#xff1f; 在傳統的Web服務器架構中&#xff0c;客戶…

解決selenium使用.get()報錯:unknown error: unsupported protocol

解決方法 將原來的&#xff1a; url "https://www.baidu.com" browser.get(url)替換為&#xff1a; url "https://www.baidu.com" browser.execute_script(f"window.location.replace({url});") # 直接平替 .get()問題解析 之前運行都是正…

【后端學前端學習記錄】學習計劃

1、個人背景 寫了足夠久的后端了&#xff0c;常用的語言基本上都接觸過&#xff0c;沒有在工作中寫過前端 一直想做一些前端的工作&#xff0c;但是前端技能不足加上自己審美不行&#xff0c;寫出的界面總是很丑 所以一直對前端做不好&#xff0c;也沒有真正下手。 2、動機 種…

Navicat 技術指引 | 連接 GaussDB 分布式

Navicat Premium&#xff08;16.3.3 Windows 版或以上&#xff09;正式支持 GaussDB 分布式數據庫。GaussDB 分布式模式更適合對系統可用性和數據處理能力要求較高的場景。Navicat 工具不僅提供可視化數據查看和編輯功能&#xff0c;還提供強大的高階功能&#xff08;如模型、結…

SLAM ORB-SLAM2(11)單目初始化

SLAM ORB-SLAM2(11)單目初始化 1. 初始化工作1.1. 單應矩陣(Homography Matrix)1.2. 基礎矩陣(Fundamental Matrix)1.3. 本質矩陣(Essential Matrix)1.4. 初始化過程2. 業務流程2.1. 創建單目初始化器2.2. 判斷連續幀的特征點數目2.3. 在兩幀中找匹配的特征點對2.4. 估…

軟件兼容性測試:保障多樣化用戶體驗的重要功能

隨著移動設備和操作系統的快速發展&#xff0c;軟件兼容性測試變得越發重要。這項測試確保軟件在不同平臺、設備和環境下都能夠正常運行&#xff0c;提供一致而穩定的用戶體驗。下面是軟件兼容性測試中的一些關鍵功能&#xff1a; 1. 跨平臺兼容性測試 在不同操作系統上運行的軟…

【flink番外篇】1、flink的23種常用算子介紹及詳細示例(3)-window、distinct、join等

Flink 系列文章 一、Flink 專欄 Flink 專欄系統介紹某一知識點&#xff0c;并輔以具體的示例進行說明。 1、Flink 部署系列 本部分介紹Flink的部署、配置相關基礎內容。 2、Flink基礎系列 本部分介紹Flink 的基礎部分&#xff0c;比如術語、架構、編程模型、編程指南、基本的…

macOS Big Sur/Mac電腦安裝vscode顯示您沒有權限來打開應用程序‘Visual Studio Code‘ 請聯系您的電腦或網絡管理員問題修復

錯誤方法 首先我以為我的權限不足。&#xff0c;需要去用戶群組里設置。結果根本不是這個的問題。 1.在系統偏好設置->用戶與群組檢查了一下我的用戶是不是管理員 結果發現是管理員 2.根據蘋果提示&#xff0c;右鍵我的文件夾->顯示簡介->最下面的共享與權限 解鎖&…

SAP UI5 walkthrough step5 Controllers

在這個章節&#xff0c;我們要做的是&#xff0c;將之前的text文本展示為一個按鈕&#xff0c;并將聲明綁定在點擊按鈕事件。 因為改的是外觀&#xff0c;所以我們修改的是view.XML webapp/view/App.view.xml <mvc:ViewcontrollerName"ui5.walkthrough.controller.A…

element中el-select多選v-model是對象數組

文章目錄 一、問題二、解決三、最后 一、問題 element中的el-select的v-model一般都是字符串或者字符串數組&#xff0c;但是有些時候后端接口要求該字段要傳對象或者對象數組&#xff0c;如果再轉換一次數據&#xff0c;對于保存配置和回顯都是吃力不討好的事情。如下所示&am…