使用numpy手写一个神经网络

news2024/9/17 4:05:11

本文主要包含以下内容:

  1. 推导神经网络的误差反向传播过程
  2. 使用numpy编写简单的神经网络,并使用iris数据集和california_housing数据集分别进行分类和回归任务,最终将训练过程可视化。

1. BP算法的推导过程

1.1 导入

img
前向传播和反向传播的总体过程。

img

神经网络的直接输出记为 Z [ l ] Z^{[l]} Z[l],表示激活前的输出,激活后的输出记为 A A A
img

第一个图像是神经网络的前向传递和反向传播的过程,第二个图像用于解释中间的变量关系,第三个图像是前向和后向过程的计算图,方便进行推导,但是第三个图左下角的 A [ l − 2 ] A^{[l-2]} A[l2]有错误,应该是 A [ l − 1 ] A^{[l-1]} A[l1]

1.2 符号表

为了方便进行推导,有必要对各个符号进行介绍

符号表

记号含义
n l n_l nl l l l层神经元个数
f l ( ⋅ ) f_l(\cdot) fl() l l l层神经元的激活函数
W l ∈ R n l − 1 × n l \mathbf{W}^l\in\R^{n_{l-1}\times n_{l}} WlRnl1×nl l − 1 l-1 l1层到第 l l l层的权重矩阵
b l ∈ R n l \mathbf{b}^l \in \R^{n_l} blRnl l − 1 l-1 l1层到第 l l l层的偏置
Z l ∈ R n l \mathbf{Z}^l \in \R^{n_l} ZlRnl l l l层的净输出,没有经过激活的输出
A l ∈ R n l \mathbf{A}^l \in \R^{n_l} AlRnl l l l层经过激活函数的输出, A 0 = X A^0=X A0=X

深层的神经网络都是由一个一个单层网络堆叠起来的,于是我们可以写出神经网络最基本的结构,然后进行堆叠得到深层的神经网络。

于是,我们可以开始编写代码,通过一个类Layer来描述单个神经网络层

class Layer:
    def __init__(self, input_dim, output_dim):
        # 初始化参数
        self.W = np.random.randn(input_dim, output_dim) * 0.01
        self.b = np.zeros((1, output_dim))
        
    def forward(self, X):
        # 前向计算
        self.Z = np.dot(X, self.W) + self.b
        self.A = self.activation(self.Z)
        return self.A
    
    def backward(self, dA, A_prev, activation_derivative):
        # 反向传播
        # 计算公式推导见下方
        m = A_prev.shape[0]
        self.dZ = dA * activation_derivative(self.Z)
        self.dW = np.dot(A_prev.T, self.dZ) / m
        self.db = np.sum(self.dZ, axis=0, keepdims=True) / m
        dA_prev = np.dot(self.dZ, self.W.T)
        return dA_prev
    
    def update_parameters(self, learning_rate):
        # 参数更新
        self.W -= learning_rate * self.dW
        self.b -= learning_rate * self.db
        

# 带有ReLU激活函数的Layer
class ReLULayer(Layer):
    def activation(self, Z):
        return np.maximum(0, Z)
    
    def activation_derivative(self, Z):
        return (Z > 0).astype(float)
    
# 带有Softmax激活函数(主要用于分类)的Layer
class SoftmaxLayer(Layer):
    def activation(self, Z):
        exp_z = np.exp(Z - np.max(Z, axis=1, keepdims=True))
        return exp_z / np.sum(exp_z, axis=1, keepdims=True)
    
    def activation_derivative(self, Z):
        # Softmax derivative is more complex, not directly used in this form.
        return np.ones_like(Z)

1.3 推导过程

权重更新的核心在于计算得到self.dWself.db,同时,为了将梯度信息不断回传,需要backward函数返回梯度信息dA_prev

需要用到的公式
Z l = W l A l − 1 + b l A l = f ( Z l ) d Z d W = ( A l − 1 ) T d Z d b = 1 Z^l = W^l A^{l-1} +b^l \\A^l = f(Z^l)\\\frac{dZ}{dW} = (A^{l-1})^T \\\frac{dZ}{db} = 1 Zl=WlAl1+blAl=f(Zl)dWdZ=(Al1)TdbdZ=1
解释:

从上方计算图右侧的反向传播过程可以看到,来自于上一层的梯度信息dA经过dZ之后直接传递到db,也经过dU之后传递到dW,于是我们可以得到dWdb的梯度计算公式如下:
d W = d A ⋅ d A d Z ⋅ d Z d W = d A ⋅ f ′ ( d Z ) ⋅ A p r e v T \begin{align}dW &= dA \cdot \frac{dA}{dZ} \cdot \frac{dZ}{dW}\\ &= dA \cdot f'(dZ) \cdot A_{prev}^T \\ \end{align} dW=dAdZdAdWdZ=dAf(dZ)AprevT
其中, f ( ⋅ ) f(\cdot) f()是激活函数, f ′ ( ⋅ ) f'(\cdot) f()是激活函数的导数, A p r e v T A_{prev}^T AprevT是当前层上一层激活输出的转置。

同理,可以得到
d b = d A ⋅ d A d Z ⋅ d Z d b = d A ⋅ f ′ ( d Z ) \begin{align}db &= dA \cdot \frac{dA}{dZ} \cdot \frac{dZ}{db}\\ &= dA \cdot f'(dZ) \\ \end{align} db=dAdZdAdbdZ=dAf(dZ)
需要仅需往前传递的梯度信息:
d A p r e v = d A ⋅ d A d Z ⋅ d Z A p r e v = d A ⋅ f ′ ( d Z ) ⋅ W T \begin{align}dA_{prev} &= dA \cdot \frac{dA}{dZ} \cdot \frac{dZ}{A_{prev}}\\ &= dA \cdot f'(dZ) \cdot W^T \\ \end{align} dAprev=dAdZdAAprevdZ=dAf(dZ)WT
所以,经过上述推导,我们可以将梯度信息从后向前传递。

分类损失函数

分类过程的损失函数最常见的就是交叉熵损失了,用来计算模型输出分布和真实值之间的差异,其公式如下:
L = − 1 N ∑ i = 1 N ∑ j = 1 C y i j l o g ( y i j ^ ) L = -\frac{1}{N}\sum_{i=1}^N \sum_{j=1}^C{y_{ij} log(\hat{y_{ij}})} L=N1i=1Nj=1Cyijlog(yij^)
其中, N N N表示样本个数, C C C表示类别个数, y i j y_{ij} yij表示第i个样本的第j个位置的值,由于使用了独热编码,因此每一行仅有1个数字是1,其余全部是0,所以,交叉熵损失每次需要对第 i i i个样本不为0的位置的概率计算对数,然后将所有所有概率取平均值的负数。

交叉熵损失函数的梯度可以简洁地使用如下符号表示:
∇ z L = y ^ − y \nabla_zL = \mathbf{\hat{y}} - \mathbf{{y}} zL=y^y

回归损失函数

均方差损失函数由于良好的性能被回归问题广泛采用,其公式如下:
L = 1 N ∑ i = 1 N ( y i − y i ^ ) 2 L = \frac{1}{N} \sum_{i=1}^N(y_i - \hat{y_i})^2 L=N1i=1N(yiyi^)2
向量形式:
L = 1 N ∣ ∣ y − y ^ ∣ ∣ 2 2 L = \frac{1}{N} ||\mathbf{y} - \mathbf{\hat{y}}||^2_2 L=N1∣∣yy^22
梯度计算:
∇ y ^ L = 2 N ( y ^ − y ) \nabla_{\hat{y}}L = \frac{2}{N}(\mathbf{\hat{y}} - \mathbf{y}) y^L=N2(y^y)

2 代码

2.1 分类代码

import numpy as np
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OneHotEncoder
import matplotlib.pyplot as plt

class Layer:
    def __init__(self, input_dim, output_dim):
        self.W = np.random.randn(input_dim, output_dim) * 0.01
        self.b = np.zeros((1, output_dim))
        
    def forward(self, X):
        self.Z = np.dot(X, self.W) + self.b     # 激活前的输出
        self.A = self.activation(self.Z)        # 激活后的输出
        return self.A
    
    def backward(self, dA, A_prev, activation_derivative):
        # 注意:梯度信息是反向传递的: l+1 --> l --> l-1
        # A_prev是第l-1层的输出,也即A^{l-1}
        # dA是第l+1的层反向传递的梯度信息
        # activation_derivative是激活函数的导数
        # dA_prev是传递给第l-1层的梯度信息
        m = A_prev.shape[0]
        self.dZ = dA * activation_derivative(self.Z)
        self.dW = np.dot(A_prev.T, self.dZ) / m
        self.db = np.sum(self.dZ, axis=0, keepdims=True) / m
        dA_prev = np.dot(self.dZ, self.W.T) # 反向传递给下一层的梯度信息
        return dA_prev
    
    def update_parameters(self, learning_rate):
        self.W -= learning_rate * self.dW
        self.b -= learning_rate * self.db

class ReLULayer(Layer):
    def activation(self, Z):
        return np.maximum(0, Z)
    
    def activation_derivative(self, Z):
        return (Z > 0).astype(float)

class SoftmaxLayer(Layer):
    def activation(self, Z):
        exp_z = np.exp(Z - np.max(Z, axis=1, keepdims=True))
        return exp_z / np.sum(exp_z, axis=1, keepdims=True)
    
    def activation_derivative(self, Z):
        # Softmax derivative is more complex, not directly used in this form.
        return np.ones_like(Z)

class NeuralNetwork:
    def __init__(self, layer_dims, learning_rate=0.01):
        self.layers = []
        self.learning_rate = learning_rate
        for i in range(len(layer_dims) - 2):
            self.layers.append(ReLULayer(layer_dims[i], layer_dims[i + 1]))
        self.layers.append(SoftmaxLayer(layer_dims[-2], layer_dims[-1]))
    
    def cross_entropy_loss(self, y_true, y_pred):
        n_samples = y_true.shape[0]
        y_pred_clipped = np.clip(y_pred, 1e-12, 1 - 1e-12)
        return -np.sum(y_true * np.log(y_pred_clipped)) / n_samples
    
    def accuracy(self, y_true, y_pred):
        y_true_labels = np.argmax(y_true, axis=1)
        y_pred_labels = np.argmax(y_pred, axis=1)
        return np.mean(y_true_labels == y_pred_labels)
    
    def train(self, X, y, epochs):
        loss_history = []
        for epoch in range(epochs):
            A = X
            # Forward propagation
            cache = [A]
            for layer in self.layers:
                A = layer.forward(A)
                cache.append(A)
            
            loss = self.cross_entropy_loss(y, A)
            loss_history.append(loss)
            
            # Backward propagation
            # 损失函数求导
            dA = A - y
            for i in reversed(range(len(self.layers))):
                layer = self.layers[i]
                A_prev = cache[i]
                dA = layer.backward(dA, A_prev, layer.activation_derivative)
            
            # Update parameters
            for layer in self.layers:
                layer.update_parameters(self.learning_rate)
            
            if (epoch + 1) % 100 == 0:
                print(f'Epoch {epoch + 1}/{epochs}, Loss: {loss:.4f}')
        
        return loss_history
    
    def predict(self, X):
        A = X
        for layer in self.layers:
            A = layer.forward(A)
        return A

# 导入数据
iris = load_iris()
X = iris.data
y = iris.target.reshape(-1, 1)

# One hot encoding
encoder = OneHotEncoder(sparse_output=False)
y = encoder.fit_transform(y)

# 分割数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 定义并训练神经网络
layer_dims = [X_train.shape[1], 100, 20, y_train.shape[1]]  # Example with 2 hidden layers
learning_rate = 0.01
epochs = 5000

nn = NeuralNetwork(layer_dims, learning_rate)
loss_history = nn.train(X_train, y_train, epochs)

# 预测和评估
train_predictions = nn.predict(X_train)
test_predictions = nn.predict(X_test)

train_acc = nn.accuracy(y_train, train_predictions)
test_acc = nn.accuracy(y_test, test_predictions)

print(f'Training Accuracy: {train_acc:.4f}')
print(f'Test Accuracy: {test_acc:.4f}')

# 绘制损失曲线
plt.plot(loss_history)
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.title('Loss Curve')
plt.show()

输出
Epoch 100/1000, Loss: 1.0983
Epoch 200/1000, Loss: 1.0980
Epoch 300/1000, Loss: 1.0975
Epoch 400/1000, Loss: 1.0960
Epoch 500/1000, Loss: 1.0891
Epoch 600/1000, Loss: 1.0119
Epoch 700/1000, Loss: 0.6284
Epoch 800/1000, Loss: 0.3711
Epoch 900/1000, Loss: 0.2117
Epoch 1000/1000, Loss: 0.1290
Training Accuracy: 0.9833
Test Accuracy: 1.0000

在这里插入图片描述
可以看到经过1000轮迭代,最终的准确率到达100%。

回归代码

import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
from sklearn.datasets import fetch_california_housing


class Layer:
    def __init__(self, input_dim, output_dim):
        self.W = np.random.randn(input_dim, output_dim) * 0.01
        self.b = np.zeros((1, output_dim))
        
    def forward(self, X):
        self.Z = np.dot(X, self.W) + self.b
        self.A = self.activation(self.Z)
        return self.A
    
    def backward(self, dA, X, activation_derivative):
        m = X.shape[0]
        self.dZ = dA * activation_derivative(self.Z)
        self.dW = np.dot(X.T, self.dZ) / m
        self.db = np.sum(self.dZ, axis=0, keepdims=True) / m
        dA_prev = np.dot(self.dZ, self.W.T)
        return dA_prev
    
    def update_parameters(self, learning_rate):
        self.W -= learning_rate * self.dW
        self.b -= learning_rate * self.db

class ReLULayer(Layer):
    def activation(self, Z):
        return np.maximum(0, Z)
    
    def activation_derivative(self, Z):
        return (Z > 0).astype(float)

class LinearLayer(Layer):
    def activation(self, Z):
        return Z
    
    def activation_derivative(self, Z):
        return np.ones_like(Z)

class NeuralNetwork:
    def __init__(self, layer_dims, learning_rate=0.01):
        self.layers = []
        self.learning_rate = learning_rate
        for i in range(len(layer_dims) - 2):
            self.layers.append(ReLULayer(layer_dims[i], layer_dims[i + 1]))
        self.layers.append(LinearLayer(layer_dims[-2], layer_dims[-1]))
    
    def mean_squared_error(self, y_true, y_pred):
        return np.mean((y_true - y_pred) ** 2)
    
    def train(self, X, y, epochs):
        loss_history = []
        for epoch in range(epochs):
            A = X
            # Forward propagation
            cache = [A]
            for layer in self.layers:
                A = layer.forward(A)
                cache.append(A)
            
            loss = self.mean_squared_error(y, A)
            loss_history.append(loss)
            
            # Backward propagation
            # 损失函数求导
            dA = -(y - A)
            for i in reversed(range(len(self.layers))):
                layer = self.layers[i]
                A_prev = cache[i]
                dA = layer.backward(dA, A_prev, layer.activation_derivative)
            
            # Update parameters
            for layer in self.layers:
                layer.update_parameters(self.learning_rate)
            
            if (epoch + 1) % 100 == 0:
                print(f'Epoch {epoch + 1}/{epochs}, Loss: {loss:.4f}')
        
        return loss_history
    
    def predict(self, X):
        A = X
        for layer in self.layers:
            A = layer.forward(A)
        return A

housing = fetch_california_housing()

# 导入数据
X = housing.data
y = housing.target.reshape(-1, 1)

# 标准化
scaler_X = StandardScaler()
scaler_y = StandardScaler()
X = scaler_X.fit_transform(X)
y = scaler_y.fit_transform(y)

# 分割数据
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 定义并训练神经网络
layer_dims = [X_train.shape[1], 50, 5, 1]  # Example with 2 hidden layers
learning_rate = 0.8
epochs = 1000

nn = NeuralNetwork(layer_dims, learning_rate)
loss_history = nn.train(X_train, y_train, epochs)

# 预测和评估
train_predictions = nn.predict(X_train)
test_predictions = nn.predict(X_test)

train_mse = nn.mean_squared_error(y_train, train_predictions)
test_mse = nn.mean_squared_error(y_test, test_predictions)

print(f'Training MSE: {train_mse:.4f}')
print(f'Test MSE: {test_mse:.4f}')

# 绘制损失曲线
plt.plot(loss_history)
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.title('Loss Curve')
plt.show()

输出
Epoch 100/1000, Loss: 1.0038
Epoch 200/1000, Loss: 0.9943
Epoch 300/1000, Loss: 0.3497
Epoch 400/1000, Loss: 0.3306
Epoch 500/1000, Loss: 0.3326
Epoch 600/1000, Loss: 0.3206
Epoch 700/1000, Loss: 0.3125
Epoch 800/1000, Loss: 0.3057
Epoch 900/1000, Loss: 0.2999
Epoch 1000/1000, Loss: 0.2958
Training MSE: 0.2992
Test MSE: 0.3071

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1703027.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于EBAZ4205矿板的图像处理:10gamma变换

基于EBAZ4205矿板的图像处理:10gamma变换 项目全部文件 会上传项目全部文件,如果没传,可以私信催我一下,最近太忙了 先看效果 我的项目中的gamma的变换系数为2.2,是会让图像整体变暗的,看右图说明我的ga…

哪款洗地机好用?洗地机十大排行榜

在智能家电飞速发展的今天,洗地机因其吸拖洗一体化的技术优势,成为越来越多家庭的清洁利器。它不仅能快速清理各种地面污渍,还能轻松处理干湿垃圾,大大提升了日常清洁的效率。可是面对市场上琳琅满目的洗地机品牌和型号&#xff0…

数据持久化第六课-ASP.NET运行机制

数据持久化第六课-ASP.NET运行机制 一.预习笔记 1.动态网页的工作机制通常分为以下几个阶段: 1)使用动态Web开发技术编写Web应用程序,并部署到Web服务器。 2)客户端通过在浏览器中输入地址,请求动态页面。 3&#…

Swift 初学者交心:在 Array 和 Set 之间我们该如何抉择?

概述 初学 Swift 且头发茂密的小码农们在日常开发中必定会在数组(Array)和集合(Set)两种类型之间的选择中“摇摆不定”,这也是人之常情。 Array 和 Set 在某些方面“亲如兄弟”,但实际上它们之间却有着“云…

关于DDos防御...别在听别人瞎扯了.....

前言 无意间刷文章的时候看到一篇文章,写的是遇到ddos,怎么用iptables封IP....... 然后我就百度搜了一下,好多都是这么说的,但是我发现,大多数人只要遭受过长期Ddos的,就不会再信网上的文章 文笔不太好&…

【Qt】深入探索Qt事件处理:从基础到高级自定义:QEvent

文章目录 前言:1. 事件的介绍2. 事件的处理2.1. 示例1: 重写鼠标进入和鼠标离开事件2.2. 示例2:当鼠标点击时,获取对应的坐标值;2.3. 鼠标释放事件2.4. 鼠标双击事件2.5. 鼠标移动事件2.6. 鼠标滚轮的滚动事件 3. 按键…

后端经典三层架构

大家好,这里是教授.F 引入: MVC 全称∶ Model 模型、View 视图、 Controller 控制器。MVC 最早出现在 JavaEE 三层中的 Web 层,它可以有效的指导WEB 层的代码如何有效分离,单独工作。 View 视图∶只负责数据和界面的显示&#…

Python I/O操作笔记

打开文件: 使用 open() 函数,其中文件路径可以是相对路径或绝对路径。 模式除了常见的 r(只读)、w(写入,会覆盖原有内容)、a(追加)外,还有一些其他组合模式&…

小度推出全球首款基于文心大模型的学习机Z30,仅售价6699元

5月27日,小度科技召开新品发布会,全球首款基于文心大模型的学习机——小度学习机Z30重磅发布。 据「TMT星球」了解,该产品基于文心大模型,重新定义了“AI老师”的能力边界,不仅是一款能为孩子提供全面、有效学习辅导的…

LINUX环境基础练习题(附带答案)

🔥 交流讨论:欢迎加入我们一起学习! 🔥 资源分享:耗时200小时精选的「软件测试」资料包 🔥 教程推荐:火遍全网的《软件测试》教程 📢欢迎点赞 👍 收藏 ⭐留言 &#x1…

嵩山为什么称为五岳之尊

在此之前,人们心目中的五岳之尊一般是东岳泰山。自此以后,观点一定会改变:五岳之尊是中岳嵩山!且听我慢慢道来。 首先将二者进行一下对比—— 中与东的对比,嵩山居中,泰山居东。东方是太阳升起的地方&#…

云原生Kubernetes: 云主机部署K8S 1.30版本 单Master架构

目录 一、实验 1.环境 2.Termius连接云主机 3.网络连通性与安全机制 4.云主机部署docker 5.云主机配置linux内核路由转发与网桥过滤 6.云主机部署cri-dockerd 7.云主机部署kubelet,kubeadm,kubectl 8.kubernetes集群初始化 9.容器网络(CNI)部署…

牛客NC67 汉诺塔问题【中等 递归 Java/Go/PHP/C++】 lintcode 169 · 汉诺塔

题目 题目链接: https://www.nowcoder.com/practice/7d6cab7d435048c4b05251bf44e9f185 https://www.lintcode.com/problem/169/ 思路 相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C…

书生·浦语大模型全链路开源体系-作业1

视频链接:书生浦语大模型全链路开源体系_哔哩哔哩_bilibili 1. LLM发展 LLM是近年来人工智能领域的一个重要发展方向。大型语言模型的历史可以追溯到2017年,当时OpenAI推出了GPT-1(Generative Pre-trained Transformer)模型,这是一个基于Transformer架构的语言生成…

论文阅读》学习了解自己:一个粗略到精细的个性化对话生成的人物感知训练框架 AAAI 2023

《论文阅读》学习了解自己:一个粗略到精细的个性化对话生成的人物感知训练框架 AAAI 2023 前言 简介研究现状任务定义模型架构Learning to know myselfLearning to avoid Misidentification损失函数实验结果消融实验 前言 亲身阅读感受分享,细节画图解释…

28【Aseprite 作图】苹果——拆解

1 画苹果框架 左边:第一行 7 第二行 2 第三 四行1 竖着7行 竖着2行 竖着1 、1 行 横着2个 横着4个 苹果可以是左右对称的,完成上述后,水平翻转到右边 2 枝叶 第一行1 左边 2 3 4 行,各1 第5行,竖着4个 再横着3个 右边 竖着3个,然后斜着2个,然后斜着1个 最上面的,两个…

RTDETR结合CVPR2024最新图像增强算法!让你的模型无惧风雨【含端到端推理脚本】

如何有效地探索雨痕的多尺度表示对于图像去雨是很重要的。与现有的基于Transformer的方法相比,这些方法主要依赖于单一尺度的雨痕外观,我们开发了一个端到端的多尺度Transformer,利用各种尺度中潜在有用的特征来促进高质量的图像重建。为了更好地探索空间变化的雨痕的常见退…

九宫格转圈圈抽奖活动,有加速,减速效果

在线访问demo和代码在底部 代码&#xff0c;复制就可以跑 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><tit…

2023idea没有VCS首次提交代码到Git

1、setting 2、vcs------>create git repository 3、右键项目----->Git------>add 4、右键项目------>git------>commit Directory 之后就会显示这个页面(下面写你提交的信息&#xff0c;就是你修改了什么) 点击commit,提交 5、Git--------->push 6、选择…

C++基础:类的继承,public,private,protected

三种继承模式 在上图中: 派生继承 三种继承模式 protected模式中 父类的公有属性和保护属性的成员在子类中都会变为保护属性,只能通过父类或者子类的成员函数调用. 代码示例: #include <iostream> #include <string> using namespace std; //protected class per…