Использование фреймворка TensorFlow2 --- построение модели

TensorFlow глубокое обучение задняя часть

Построение модели TF

В целом, общие проблемы контролируемого машинного обучения делятся на две категории:Классификацияивозвращение, когда мы используем Tensorflow для решения этих проблем, нам приходится строить собственную сетевую модель, но существуют разные методы построения моделей для разных уровней API TensorFlow. API более низкого уровня является более гибким, и вы можете более свободно добавлять желаемый контент, но сложность кодирования возрастает; напротив, API более высокого уровня имеет лучшую инкапсуляцию и может быть реализован с помощью нескольких простых строк кода. Конструкция модели, но гибкость неизбежно снижается. Сегодня я расскажу о нескольких способах построения сети с разными уровнями API.

1. Проблема регрессии

1.1 Генерация данных

Сначала мы должны сами разработать задачу регрессии, то есть построить уравнение, а затем обучить сеть, чтобы оно соответствовало ему.

Мы знакомы с линейными уравнениями:Y=W*X+bY=W*X+b

Здесь мы генерируем 200 данных, X — равномерное распределение между (-10, 10), W — (2, —2), b=3, и добавляется шум.

# 设置随机数种子
tf.random.set_seed(0)
# 样本数
n=200

# 生成测试用数据集
# Y=WX+b+noise
# 相当于两个方程: Y=2X+3+noise;Y=-3X+3+noise
X = tf.random.uniform([n,2],minval=-10,maxval=10) 
w0 = tf.constant([[2.0],[-2.0]])
b0 = tf.constant([[3.0]])
Y = X@w0 + b0 + tf.random.normal([n,1],mean = 0.0,stddev= 2.0)

Сгенерированные данные выглядят так

plt.figure(figsize = (12,5))
ax1 = plt.subplot(121)
ax1.scatter(X[:,0],Y[:,0], c = "b")
plt.xlabel("x1")
plt.ylabel("y",rotation = 0)

ax2 = plt.subplot(122)
ax2.scatter(X[:,1],Y[:,0], c = "g")
plt.xlabel("x2")
plt.ylabel("y",rotation = 0)
plt.show()

image-20211209104020670

Для простоты я не буду делить обучающую выборку, тестовую выборку и т.д., а напрямую использовать все данные для обучения и предсказания. Далее нам нужно создать генератор данных для генерации X и Y в каждом пакетном_размере.

Общая идея генератора данных такова:

  1. Сначала случайным образом перемешайте индексы данных

  2. Пройдите данные, каждый размер_пакета используется в качестве разделителя, и получите скремблированный фрагмент нижнего индекса (размер — размер пакета).

  3. Используйте функцию tf.gather() для объединения X и Y со случайными индексами, полученными на предыдущем шаге, yield возвращается в генератор

Функция tf.gather(params,indices,axis=0) возвращает срез соответствующего элемента из params в соответствии с нижним индексом index

# 构建数据生成器
# 其中tf.gather(params,indices,axis=0)是根据indices下标从params中返回对应元素的切片


def data_iter(features, labels, batch_size=8):
    num_examples = len(features)
    indices = list(range(num_examples))
    np.random.shuffle(indices)
    for i in range(0, num_examples, batch_size):
        indexs = indices[i: min(i + batch_size, num_examples)]
        yield tf.gather(features,indexs), tf.gather(labels,indexs)

# 测试数据生成器效果   
batch_size = 8
(datas,labels) = next(data_iter(X,Y,batch_size))
print(datas)

image-20211209105414004

На данный момент генерация данных и построение генератора реализованы, и следующим шагом является определение и обучение модели, что является ключом к сегодняшнему дню.

1.2 Реализация API высокого уровня

Прежде всего, я хочу использовать наиболее распространенный способ записи для построения модели, чтобы можно было легко построить сеть, не привлекая слишком много подробных знаний, что более приемлемо в начале.

Здесь я устанавливаю скорость обучения на 0,001, batch_size=20, epochs=100, и оптимизатор выбирает SGD.

Здесь я хотел бы сказать несколько дополнительных слов об оптимизаторе. Оптимизатор является ключом к оптимизации параметров, и можно себе представить его важность в обучении модели. Большую часть времени мы выбираем Адама в качестве оптимизатора, но, по результатам моей собственной практики, Адам медленно подходит, и эффект не очень хороший (эффект здесь относится к конечному размеру убытка), и требуется больше, чтобы достичь такая же потеря, раз итеративное обучение, и часто потеря выше, чем SGD. Затем я отправился изучать новые методы, предложенные оптимизатором в последние годы, такие какАггМо, Аполлон, диффГрад, Ягненок, МАДГРАД... У меня более глубокое понимание оптимизатора, и мне также интересно, есть ли лучший способ его оптимизировать. Конечно, некоторые из упомянутых выше методов не имеют соответствующих API в Tensorflow или оптимизаторов в Pytorch, но некоторые люди собрали и построили стороннюю библиотеку оптимизатора, совместимую с Pytorch.pytorch-optimizer, вы можете попробовать разницу между новым оптимизатором и классическим оптимизатором.

Хорошо, вернемся к сегодняшней теме, начнем строить сеть

Здесь мы знаем, что уравнение является линейным уравнением, поэтому нам нужен только линейный слой, и нам не нужно добавлять другие нелинейные функции активации.

lr3=0.001
optimizer=optimizers.SGD(learning_rate=lr3)


model3=models.Sequential()
model3.add(layers.Dense(1,input_shape=(2,)))
model3.compile(optimizer=optimizer,loss='mse',metrics=['mae'])
model3.fit(X,Y,batch_size=20,epochs=100)

tf.print(f"w={model3.layers[0].kernel}")
tf.print(f"b={model3.layers[0].bias}")

image-20211209112156183

Окончательная потеря составляет 3,62, а эффект подгонки модели выглядит следующим образом.

w,b = model3.variables

plt.figure(figsize = (12,5))
ax1 = plt.subplot(121)
ax1.scatter(X[:,0],Y[:,0], c = "b",label = "samples")
ax1.plot(X[:,0],w[0]*X[:,0]+b[0],"-r",linewidth = 5.0,label = "model")
ax1.legend()
plt.xlabel("x1")
plt.ylabel("y",rotation = 0)

ax2 = plt.subplot(122)
ax2.scatter(X[:,1],Y[:,0], c = "g",label = "samples")
ax2.plot(X[:,1],w[1]*X[:,1]+b[0],"-r",linewidth = 5.0,label = "model")
ax2.legend()
plt.xlabel("x2")
plt.ylabel("y",rotation = 0)

plt.show()

image-20211209112334511

1.3 Промежуточная реализация API

Главной особенностью высокоуровневой реализации API является ее удобство и простота. Построение модели и обучение можно выполнить всего несколькими строками кода. Однако использование API промежуточного уровня уже не так зависит от упакованных интерфейсов, и некоторые функции могут быть реализованы сами по себе.

# 学习率
lr2=0.001
# 批次大小
batch_size2=30

model2=layers.Dense(1,input_shape=(2,))
model2.loss_func=losses.mean_squared_error
model2.optimizer=optimizers.SGD(learning_rate=lr2)

Как и в высокоуровневой реализации API, здесь уже не нужны модели, а более простой слой. После настройки функции потерь и оптимизатора следующим шагом будет самостоятельное написание процесса обучения.

Сначала напишите функцию для обучения эпох, а затем используйте цикл для обучения времени эпох.

@tf.function
def train_step(model, features, labels):
    with tf.GradientTape() as tape:
        predictions = model(features)
        loss = model.loss_func(tf.reshape(labels,[-1]), tf.reshape(predictions,[-1]))
    grads = tape.gradient(loss,model.variables)
    model.optimizer.apply_gradients(zip(grads,model.variables))
    return loss

# 测试train_step效果
features,labels = next(data_iter(X,Y,batch_size2))
train_step(model2,features,labels)

С помощью упомянутого выше автоматического дифференцирования мы получаем предсказанное значение и потери каждого времени вперед, а затем берем частную производную параметров модели по потерям, чтобы получить градиент каждого параметра, и, наконец, оптимизатор обновляется в соответствии с градиентом каждого параметра

image-20211209135051457

С обучением для каждой эпохи модель можно обучать итеративно, используя только циклы.

def train_model(model,epochs):
    for epoch in tf.range(1,epochs+1):
        loss = tf.constant(0.0)
        for features, labels in data_iter(X,Y,batch_size2):
            loss = train_step(model,features,labels)
        if epoch%50==0:
            tf.print(f"=========================================================   {time.asctime(time.localtime(time.time()))}")
            tf.print("epoch =",epoch,"loss = ",loss)
            tf.print("w =",model.variables[0])
            tf.print("b =",model.variables[1])
train_model(model2,epochs = 200)

w,b = model2.variables

plt.figure(figsize = (12,5))
ax1 = plt.subplot(121)
ax1.scatter(X[:,0],Y[:,0], c = "b",label = "samples")
ax1.plot(X[:,0],w[0]*X[:,0]+b[0],"-r",linewidth = 5.0,label = "model")
ax1.legend()
plt.xlabel("x1")
plt.ylabel("y",rotation = 0)



ax2 = plt.subplot(122)
ax2.scatter(X[:,1],Y[:,0], c = "g",label = "samples")
ax2.plot(X[:,1],w[1]*X[:,1]+b[0],"-r",linewidth = 5.0,label = "model")
ax2.legend()
plt.xlabel("x2")
plt.ylabel("y",rotation = 0)

plt.show()

image-20211209135220831

Окончательная потеря составляет около 3,46.

1.4 Реализация самого простого API

В приведенных выше двух реализациях Dense избавляет нас от необходимости создаватьy=w*x+by=w*x+b

image-20211209140101007

Оптимизатор позволяет нам реализовать алгоритм оптимизации без какой-либо инкапсуляции, просто используя самый простой API и некоторые базовые знания для его реализации.

Сделай сначала самy=w*x+by=w*x+b, объявить две переменные w, b, а затем определить формулу расчета прямого распространенияx@w+bx@w+b, это просто удаление функции активации в Dense; затем определите функцию потерь или используйте среднеквадратичную ошибку(groudtruthpredict)2N\frac{(groudtruth-predict)^2}{N}, чтобы исключить показатель степени после вывода, его обычно умножают на12\frac{1}{2}

# 构建wx+b拟合函数
w = tf.Variable(tf.random.normal(w0.shape))
b = tf.Variable(tf.zeros_like(b0,dtype = tf.float32))

# 定义模型
class LinearRegression:     
    #正向传播
    def __call__(self,x): 
        return x@w + b

    # 损失函数
    def loss_func(self,y_true,y_pred):  
        return tf.reduce_mean((y_true - y_pred)**2/2)

model = LinearRegression()

Таким образом создается базовая модель линейной регрессии, и следующим шагом является оптимизация параметров модели, то есть обучение модели. Количество параметров модели невелико, поэтому здесь мы используем самый простой метод градиентного спуска. Градиент основан на автоматическом выводе, а формула обновления градиента:w=wα*wJ(w,b)w=w-\alpha*\frac{\partial}{\partial w}J(w,b)

вwwэто вес,α\alphaэто скорость обучения, которая представляет собой размер шага каждого сброса обновления,J(w,b)J(w,b)это функция потерь, среднеквадратическая ошибка выше, а другие части обучения аналогичны приведенным выше частям обучения.

# 学习率
lr=0.001
# 批次大小
batch_size=20

@tf.function
def train_step(model, features, labels):
    # 用于自动微分
    with tf.GradientTape() as tape:
        predictions = model(features)
        loss = model.loss_func(labels, predictions)
    # 反向传播求梯度,即各系数的偏导数
    dloss_dw,dloss_db = tape.gradient(loss,[w,b])
    # 梯度下降法更新参数
    w.assign(w - lr*dloss_dw)
    b.assign(b - lr*dloss_db)
    return loss


def train_model(model,epochs):
    for epoch in tf.range(1,epochs+1):
        for features, labels in data_iter(X,Y,batch_size):
            loss = train_step(model,features,labels)
        if epoch%50==0:
            tf.print(f"=========================================================   {time.asctime(time.localtime(time.time()))}")
            tf.print("epoch =",epoch,"loss = ",loss)
            tf.print("w =",w)
            tf.print("b =",b)

train_model(model,epochs = 200)

image-20211209143236663

Окончательная потеря составляет всего около 2,00.Эффект простого градиентного спуска лучше, чем у других продвинутых оптимизаторов.Может быть, уравнение слишком простое.

2. Проблемы классификации

Процесс проблемы классификации и проблемы регрессии примерно аналогичен, но сначала генерируются данные, а затем модель строится и обучается с помощью API. Единственное отличие состоит в том, что для задачи регрессии не требуется функция активации, поскольку это линейное распределение, а градиентный спуск также можно использовать для хорошей сходимости и подгонки во время обучения; но классификация является нелинейной, поэтому необходимо добавить функцию активации к каждый слой, и во время обучения функция потерь больше не является MSE, а в качестве функции потерь используется перекрестная энтропия.

Почему MSE больше не применяется?

img

Это кривая исходной функции и ее производная кривая сигмовидной функции активации.Если мы используем MSE в качестве функции потерь, если MSE очень велика в начале (что часто бывает), то значение производной сигмовидной функции в начале почти 0. Это когда градиентный спуск почти не имеет градиента (Градиент исчезает), обновление параметра не может быть выполнено, и финальное обучение завершается сбоем.

В настоящее время перекрестная энтропия является хорошей функцией потери классификации.

H(p,q)=i=1np(xi)log(q(xi))H(p,q)=-\sum_{i=1}^{n}p(x_i)log(q(x_i))

вp(xi)p(x_i)- вероятность наступления события,q(xi)q(x_i)прогнозируемая вероятность

Для бинарной классификации есть только две метки, 0 или 1, а затем q = 1-p, поэтому приведенную выше формулу можно упростить до

Cross_Entropy(p,q)=(plogq+(1p)log(1q))Cross\_Entropy(p,q)=-(plog{q}+(1-p)log(1-q))

Сказав основные принципы, давайте приступим к их реализации прямо сейчас

2.1 Генерация данных

#正负样本数量
n_positive,n_negative = 2000,2000

#生成正样本, 小圆环分布
r_p = 5.0 + tf.random.truncated_normal([n_positive,1],0.0,1.0)
theta_p = tf.random.uniform([n_positive,1],0.0,2*np.pi) 
Xp = tf.concat([r_p*tf.cos(theta_p),r_p*tf.sin(theta_p)],axis = 1)
Yp = tf.ones_like(r_p)

#生成负样本, 大圆环分布
r_n = 8.0 + tf.random.truncated_normal([n_negative,1],0.0,1.0)
theta_n = tf.random.uniform([n_negative,1],0.0,2*np.pi) 
Xn = tf.concat([r_n*tf.cos(theta_n),r_n*tf.sin(theta_n)],axis = 1)
Yn = tf.zeros_like(r_n)

#汇总样本
X = tf.concat([Xp,Xn],axis = 0)
Y = tf.concat([Yp,Yn],axis = 0)


#可视化
plt.figure(figsize = (6,6))
plt.scatter(Xp[:,0].numpy(),Xp[:,1].numpy(),c = "r")
plt.scatter(Xn[:,0].numpy(),Xn[:,1].numpy(),c = "g")
plt.legend(["正样本","负样本"])
plt.show()

Функция tf.random.truncated_normal() предназначена для усечения нормального распределения, то есть для ограничения диапазона случайно сгенерированных нормально распределенных данных в(μ2δ,μ+2δ)(\mu-2\delta,\mu+2\delta), окончательный образец выглядит так

image-20211209185730833

2.2 Реализация API высокого уровня

Использование высокоуровневого API все еще может быть реализовано в нескольких строках кода.

model3=models.Sequential()
model3.add(layers.Dense(4,input_shape=(2,),activation='relu'))
model3.add(layers.Dense(8,activation='relu'))
model3.add(layers.Dense(1,activation='sigmoid'))

model3.summary()

image-20211209214838280

optimizer = optimizers.SGD(learning_rate=0.001)
loss_func = tf.keras.losses.BinaryCrossentropy()
model3.compile(optimizer=optimizer,loss=loss_func,metrics=['acc'])
model3.fit(X,Y,batch_size=100,epochs=50)


fig, (ax1,ax2) = plt.subplots(nrows=1,ncols=2,figsize = (12,5))
ax1.scatter(Xp[:,0].numpy(),Xp[:,1].numpy(),c = "r")
ax1.scatter(Xn[:,0].numpy(),Xn[:,1].numpy(),c = "g")
ax1.legend(["positive","negative"]);
ax1.set_title("y_true");

Xp_pred = tf.boolean_mask(X,tf.squeeze(model3(X)>=0.5),axis = 0)
Xn_pred = tf.boolean_mask(X,tf.squeeze(model3(X)<0.5),axis = 0)

ax2.scatter(Xp_pred[:,0].numpy(),Xp_pred[:,1].numpy(),c = "r")
ax2.scatter(Xn_pred[:,0].numpy(),Xn_pred[:,1].numpy(),c = "g")
ax2.legend(["positive","negative"]);
ax2.set_title("y_pred")
plt.show()

image-20211209214932004

2.3 Промежуточная реализация API

Основная причина использования API среднего уровня заключается в том, чтобы больше не полагаться на models.Sequential(), определить глубокую нейронную сеть самостоятельно, а затем написать в ней слои и прямое распространение. После создания экземпляра используйте двоичную перекрестную энтропию и оптимизатор для оптимизации и обучения модели.

class DNNModel2(tf.Module):
    def __init__(self,name = None):
        super(DNNModel2, self).__init__(name=name)
        self.dense1 = layers.Dense(4,activation = "relu") 
        self.dense2 = layers.Dense(8,activation = "relu")
        self.dense3 = layers.Dense(1,activation = "sigmoid")


    # 正向传播
    @tf.function(input_signature=[tf.TensorSpec(shape = [None,2], dtype = tf.float32)])  
    def __call__(self,x):
        x = self.dense1(x)
        x = self.dense2(x)
        y = self.dense3(x)
        return y

model2 = DNNModel2()
model2.loss_func = losses.binary_crossentropy
model2.metric_func = metrics.binary_accuracy
model2.optimizer = optimizers.Adam(learning_rate=0.001)

(features,labels) = next(data_iter(X,Y,batch_size))
predictions = model2(features)
loss = model2.loss_func(tf.reshape(labels,[-1]),tf.reshape(predictions,[-1]))
metric = model2.metric_func(tf.reshape(labels,[-1]),tf.reshape(predictions,[-1]))

tf.print("初始损失:",loss)
tf.print("初始化准确率",metric)

Метод обучения модели аналогичен задаче регрессии выше.

@tf.function
def train_step(model, features, labels):
    with tf.GradientTape() as tape:
        predictions = model(features)
        loss = model.loss_func(tf.reshape(labels,[-1]), tf.reshape(predictions,[-1]))
    grads = tape.gradient(loss,model.trainable_variables)
    model.optimizer.apply_gradients(zip(grads,model.trainable_variables))

    metric = model.metric_func(tf.reshape(labels,[-1]), tf.reshape(predictions,[-1]))

    return loss,metric

# 测试train_step效果
(features,labels) = next(data_iter(X,Y,batch_size))
train_step(model2,features,labels)

def train_model(model,epochs):
    for epoch in tf.range(1,epochs+1):
        loss, metric = tf.constant(0.0),tf.constant(0.0)
        for features, labels in data_iter(X,Y,batch_size):
            loss,metric = train_step(model,features,labels)
        if epoch%10==0:
            tf.print(f"=========================================================   {time.asctime(time.localtime(time.time()))}")
            tf.print("epoch =",epoch,"loss = ",loss, "accuracy = ",metric)
train_model(model2,epochs = 50)


fig, (ax1,ax2) = plt.subplots(nrows=1,ncols=2,figsize = (12,5))
ax1.scatter(Xp[:,0].numpy(),Xp[:,1].numpy(),c = "r")
ax1.scatter(Xn[:,0].numpy(),Xn[:,1].numpy(),c = "g")
ax1.legend(["positive","negative"]);
ax1.set_title("y_true");

Xp_pred = tf.boolean_mask(X,tf.squeeze(model2(X)>=0.5),axis = 0)
Xn_pred = tf.boolean_mask(X,tf.squeeze(model2(X)<0.5),axis = 0)

ax2.scatter(Xp_pred[:,0].numpy(),Xp_pred[:,1].numpy(),c = "r")
ax2.scatter(Xn_pred[:,0].numpy(),Xn_pred[:,1].numpy(),c = "g")
ax2.legend(["positive","negative"]);
ax2.set_title("y_pred")
plt.show()

image-20211209215733378

2.4 Реализация низкоуровневого API

В приведенной выше реализации, за исключением конечного уровня классификации, каждый уровень на самом деле выполняет только одну задачу, то есть вычисление.Y=relu(w*x+b)Y=relu(w*x+b), последний слой меняет relu на сигмовидный для классификации, а остальные остаются неизменными, поэтому в низкоуровневой реализации мы честно определяем переменные, а затем распространяемся вперед, а обратно вычисляем и обновляем эти параметры. Затем loss_func, представляющий собой бинарную перекрестную энтропию, использует упрощенную формулу, о которой мы говорили в начале.

class DNNModel(tf.Module):
    def __init__(self,name = None):
        super(DNNModel, self).__init__(name=name)
        self.w1 = tf.Variable(tf.random.truncated_normal([2,4]),dtype = tf.float32)
        self.b1 = tf.Variable(tf.zeros([1,4]),dtype = tf.float32)
        self.w2 = tf.Variable(tf.random.truncated_normal([4,8]),dtype = tf.float32)
        self.b2 = tf.Variable(tf.zeros([1,8]),dtype = tf.float32)
        self.w3 = tf.Variable(tf.random.truncated_normal([8,1]),dtype = tf.float32)
        self.b3 = tf.Variable(tf.zeros([1,1]),dtype = tf.float32)


    # 正向传播
    @tf.function(input_signature=[tf.TensorSpec(shape = [None,2], dtype = tf.float32)])  
    def __call__(self,x):
        x = tf.nn.relu(x@self.w1 + self.b1)
        x = tf.nn.relu(x@self.w2 + self.b2)
        y = tf.nn.sigmoid(x@self.w3 + self.b3)
        return y

    # 损失函数(二元交叉熵)
    @tf.function(input_signature=[tf.TensorSpec(shape = [None,1], dtype = tf.float32),
                              tf.TensorSpec(shape = [None,1], dtype = tf.float32)])  
    def loss_func(self,y_true,y_pred):  
        #将预测值限制在 1e-7 以上, 1 - 1e-7 以下,避免log(0)错误
        eps = 1e-7
        y_pred = tf.clip_by_value(y_pred,eps,1.0-eps)
        bce = - y_true*tf.math.log(y_pred) - (1-y_true)*tf.math.log(1-y_pred)
        return  tf.reduce_mean(bce)

    # 评估指标(准确率)
    @tf.function(input_signature=[tf.TensorSpec(shape = [None,1], dtype = tf.float32),
                              tf.TensorSpec(shape = [None,1], dtype = tf.float32)]) 
    def metric_func(self,y_true,y_pred):
        y_pred = tf.where(y_pred>0.5,tf.ones_like(y_pred,dtype = tf.float32),
                          tf.zeros_like(y_pred,dtype = tf.float32))
        acc = tf.reduce_mean(1-tf.abs(y_true-y_pred))
        return acc
      
batch_size = 10
(features,labels) = next(data_iter(X,Y,batch_size))

# 模型实例化
model = DNNModel()
predictions = model(features)

loss = model.loss_func(labels,predictions)
metric = model.metric_func(labels,predictions)

tf.print("初始损失:",loss)
tf.print("初始化准确率",metric)

image-20211209223606377

Все обучаемые параметры, то есть те параметры, которые мы определяем сами, обучаются по loss_func, а метод обновления по-прежнемуw=wα*ww=w-\alpha *\frac{\partial}{\partial w}

# 开始训练
lr=0.005

@tf.function
def train_step(model, features, labels):

    # 正向传播求损失
    with tf.GradientTape() as tape:
        predictions = model(features)
        loss = model.loss_func(labels, predictions) 

    # 反向传播求梯度
    grads = tape.gradient(loss, model.trainable_variables)

    # 执行梯度下降
    for p, dloss_dp in zip(model.trainable_variables,grads):
        p.assign(p - lr*dloss_dp)

    # 计算评估指标
    metric = model.metric_func(labels,predictions)

    return loss, metric


def train_model(model,epochs):
    for epoch in tf.range(1,epochs+1):
        for features, labels in data_iter(X,Y,150):
            loss,metric = train_step(model,features,labels)
        if epoch%100==0:
            tf.print(f"=========================================================   {time.asctime(time.localtime(time.time()))}")
            tf.print("epoch =",epoch,"loss = ",loss, "accuracy = ", metric)


train_model(model,epochs = 600)

fig, (ax1,ax2) = plt.subplots(nrows=1,ncols=2,figsize = (12,5))
ax1.scatter(Xp[:,0],Xp[:,1],c = "r")
ax1.scatter(Xn[:,0],Xn[:,1],c = "g")
ax1.legend(["positive","negative"]);
ax1.set_title("y_true");

Xp_pred = tf.boolean_mask(X,tf.squeeze(model(X)>=0.5),axis = 0)
Xn_pred = tf.boolean_mask(X,tf.squeeze(model(X)<0.5),axis = 0)

ax2.scatter(Xp_pred[:,0],Xp_pred[:,1],c = "r")
ax2.scatter(Xn_pred[:,0],Xn_pred[:,1],c = "g")
ax2.legend(["positive","negative"]);
ax2.set_title("y_pred")
plt.show()

image-20211209225045830

Заканчивать

Сегодня я снова рассмотрел, как построить модель. Есть некоторые важные детали, такие как размер input_shape и значение значения в Dense. Эти часто используемые API будут подробно обсуждаться позже. Изучив так много способов построения моделей, вы станете более опытными в настройке параметров и углубите свое понимание принципов реализации модели. Прогресс все еще немного медленнее, чем я ожидал, и после этого он должен быть быстрее.