Градиент

Задача:
Напишите функцию gradient(color), создающую графический файл с плавным переходом цвета. Файл должен содержать прямоугольник длиной 512 пикселей (по 2 пикселя на каждый оттенок) и высотой 200 пикселей.
В функцию передается обозначение цвета, градиент которого надо построить. Варианты цветов: R, G и B. Обозначение может быть введено в любом регистре.
Вот мой код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
from PIL import Image, ImageDraw def gradient(choose): new_image = Image.new("RGB", (512, 200), (0, 0, 0)) draw = ImageDraw.Draw(new_image) r = 0 g = 0 b = 0 for i in range(new_image.size[0]): draw.line((i, 0, i, 512), fill=(r, g, b), width=2) if i % 4 == 0: if choose == "R": r += 2 elif choose == "G": g += 2 else: b += 2 new_image.save("res.png", "PNG")
Файл res.png — это то, что получается у меня (1).
А другой — это то, что должно получиться (2).
Не знаю, что нужно подправить.
Как создать полупрозрачный градиент?

Сейчас мне нужно наложить на back_picture градиент с особыми характеристиками:
- Вертикальный
- Крайняя нижняя точка имеет цвет #2A303D и 100% непрозрачности
- Крайняя верхняя точка имеет цвет #2A303D и 50% непрозрачности
- Очень важно! Центр градиента не расположен посередине, т.е. нижняя и верхняя точки неравноправны. Центр градиента смещен в сторону верхней точки, значит, нижняя занимает доминирующее положение. Обозначил точку центра градиента красной стрелкой.

Нарисовал картинку, которая показывает разницу наглядно. Цифрой 1 обозначил градиент со смещенным центром(как мне надо), цифрой 2 обозначил градиент без смещенного центра(как мне НЕ надо):

Градиент с цифрой 1 является моей целью
Покажу цветом, как градиент может быть смещен в сторону какой-либо точки, чтобы было понятнее. В первом случае градиент смещен в сторону нижней точки, во втором случае — в сторону верхней точки:

Работая в illustrator’е я заметил, что png запоминает уровень прозрачности пикселя. Это натолкнуло меня на мысль, что картинку градиента можно просто сохранить и расположить над back_picture .

Если открыть эту картинку в каком-нибудь графическом редакторе, полупрозрачные пиксели будут действительно полупрозрачными. Но, к моему большому разочарованию, в окне приложения градиент отображается сплошным квадратом цвета #2A303D:

Мне нужно как-то создать градиент с точно такими же характеристиками и расположить его ровно над back_picture (важно, чтобы не было видно стыков), сохранив возможность менять изображение в back_picture . Пожалуйста, помогите мне сделать это
from PyQt5 import QtCore, QtWidgets, QtGui class BackPicture(QtWidgets.QLabel): def __init__(self, picture, x, *args, **kwargs): super(BackPicture, self).__init__(*args, **kwargs) self.setFixedSize(x, x) self.x = x self.setPicture(picture) def setPicture(self, picture): self.setPixmap(QtGui.QPixmap(picture).scaled(self.x, self.x, QtCore.Qt.KeepAspectRatio)) class MyWindow(QtWidgets.QWidget): def __init__(self, parent = None): super().__init__(parent) self.index = 1 btn = QtWidgets.QPushButton('Remake', clicked = self.remake) self.container = QtWidgets.QWidget() self.container.setStyleSheet('background: #2A303D;') self.container.setMinimumHeight(300) self.container.setFixedWidth(300) main_box = QtWidgets.QHBoxLayout(self) main_box.addWidget(btn) main_box.addWidget(self.container) box = QtWidgets.QVBoxLayout(self.container) box.setContentsMargins(0, 0, 0, 0) self.back_picture = BackPicture('picture3.jpg', 300, self.container) self.back_picture.move(0, 0) #self.gradient = BackPicture('gradient.png', 300, self.container) #self.gradient.move(0, 0) НЕ РАБОТАЕТ label = QtWidgets.QLabel('Text number 1') label.setStyleSheet(qss) box.addStretch(6) box.addWidget(label, alignment = QtCore.Qt.AlignCenter) box.addStretch(1) def remake(self): if self.index == 1: self.back_picture.setPicture('picture2.png') self.index = 2 else: self.back_picture.setPicture('picture3.jpg') self.index = 1 qss = '''QLabel < color: white; font: bold 16px; background: transparent; >''' if __name__ == '__main__': import sys app = QtWidgets.QApplication(sys.argv) window = MyWindow() window.setWindowTitle(' ') window.show() sys.exit(app.exec_())
Градиентный спуск с помощью простого набора данных
На уроках математики в школах учат, что уравнение прямой имеет вид y = mx + c (обозначения могут отличаться в зависимости от книг), где y — точка на оси y, x — точка на оси x, m — наклон, а c — пересечение на оси y. Если пересечение по оси y равно 0, значение c можно игнорировать. В машинном обучении это уравнение является базовой задачей линейной регрессии. Если в двумерной плоскости есть только одна линия, наклон и пересечение можно найти с помощью формул, подставив их вместо m и c и решив уравнение. Следовательно, у него есть единственное решение.
На самом деле каждая задача машинного обучения включает в себя сотни или тысячи уравнений, которые чаще всего не согласуются ни с одним решением. Каждое уравнение — это “Наблюдение”, а каждая ось — “Особенность”. Когда система уравнений содержит больше наблюдений, чем признаков, она называется переопределённой системой. Следовательно, большинство проблем машинного обучения связаны с чрезмерно детерминированной системой. Градиентный спуск используется для нахождения решения (коэффициента) для каждого признака, с которым могут согласиться все наблюдения, что сведёт к минимуму разницу между наблюдаемыми и прогнозируемыми результатами. Разница рассчитывается с использованием множества «Функций затрат”. Одним из них является среднеквадратичная ошибка (MSE), которая используется в задаче ML этой статьи.
Для этой задачи градиентного спуска подготовлен простой синтетический набор данных. Все блоки кода написаны на языке программирования Python. Программа начинается с импорта необходимых пакетов Python:
Градиентный спуск в Python

Рабочая область функции (заданный интервал) разбита на несколько точек. Выбраны точки локальных минимумов. После этого все координаты передаются функции в качестве аргументов и выбирается аргумент, дающий наименьшее значение. Затем применяется метод градиентного спуска.
Реализация
Прежде всего, numpy необходим для функций sinus, cosinus и exp. Также необходимо добавить matplotlib для построения графиков.
import numpy as np import matplotlib.pyplot as plot
radius = 8 # working plane radius centre = (global_epsilon, global_epsilon) # centre of the working circle arr_shape = 100 # number of points processed / 360 step = radius / arr_shape # step between two points
arr_shape должна быть 100, потому что, если она больше, программа начинает работать значительно медленнее. И не может быть меньше, иначе это испортит расчеты.
Функция, для которой рассчитывается минимум:
def differentiable_function(x, y): return np.sin(x) * np.exp((1 - np.cos(y)) ** 2) + \ np.cos(y) * np.exp((1 - np.sin(x)) ** 2) + (x - y) ** 2
Затем выбирается приращение аргумента:

Поскольку предел аргумента стремится к нулю, точность должна быть небольшой по сравнению с радиусом рабочей плоскости:
global_epsilon = 0.000000001 # argument increment for derivative
Для дальнейшего разбиения плоскости необходим поворот вектора:

Если вращение применяется к вектору (x, 0), повернутый вектор будет вычисляться следующим образом:
def rotate_vector(length, a): return length * np.cos(a), length * np.sin(a)
Расчет производной по оси Y, где эпсилон — значение y:
def derivative_y(epsilon, arg): return (differentiable_function(arg, epsilon + global_epsilon) - differentiable_function(arg, epsilon)) / global_epsilon
Вычисление производной по оси X, где эпсилон — значение x:
def derivative_x(epsilon, arg): return (differentiable_function(global_epsilon + epsilon, arg) - differentiable_function(epsilon, arg)) / global_epsilon

Поскольку градиент вычисляется для 2D-функции, k равно нулю
gradient = derivative_x(x, y) + derivative_y(y, x)
Схема генерации точек

Возвращаемое значение представляет собой массив приблизительных локальных минимумов.
Локальный минимум распознается по смене знака производной с минуса на плюс. Подробнее об этом здесь: https://en.wikipedia.org/wiki/Maxima_and_minima
def calculate_flip_points(): flip_points = np.array([0, 0]) points = np.zeros((360, arr_shape), dtype=bool) cx, cy = centre for i in range(arr_shape): for alpha in range(360): x, y = rotate_vector(step, alpha) x = x * i + cx y = y * i + cy points[alpha][i] = derivative_x(x, y) + derivative_y(y, x) > 0 if not points[alpha][i - 1] and points[alpha][i]: flip_points = np.vstack((flip_points, np.array([alpha, i - 1]))) return flip_points
Выбор точки из flip_points, значение функции от которой минимально:
def pick_estimates(positions): vx, vy = rotate_vector(step, positions[1][0]) cx, cy = centre best_x, best_y = cx + vx * positions[1][1], cy + vy * positions[1][1] for index in range(2, len(positions)): vx, vy = rotate_vector(step, positions[index][0]) x, y = cx + vx * positions[index][1], cy + vy * positions[index][1] if differentiable_function(best_x, best_y) > differentiable_function(x, y): best_x = x best_y = y for index in range(360): vx, vy = rotate_vector(step, index) x, y = cx + vx * (arr_shape - 1), cy + vy * (arr_shape - 1) if differentiable_function(best_x, best_y) > differentiable_function(x, y): best_x = x best_y = y return best_x, best_y
Метод градиентного спуска:
def gradient_descent(best_estimates, is_x): derivative = derivative_x if is_x else derivative_y best_x, best_y = best_estimates descent_step = step value = derivative(best_y, best_x) while abs(value) > global_epsilon: descent_step *= 0.95 best_y = best_y - descent_step \ if derivative(best_y, best_x) > 0 else best_y + descent_step value = derivative(best_y, best_x) return best_y, best_x
Нахождение точки минимума:
def find_minimum(): return gradient_descent(gradient_descent(pick_estimates(calculate_flip_points()), False), True)
Формирование сетки точек для построения:
def get_grid(grid_step): samples = np.arange(-radius, radius, grid_step) x, y = np.meshgrid(samples, samples) return x, y, differentiable_function(x, y)
def draw_chart(point, grid): point_x, point_y, point_z = point grid_x, grid_y, grid_z = grid plot.rcParams.update(< 'figure.figsize': (4, 4), 'figure.dpi': 200, 'xtick.labelsize': 4, 'ytick.labelsize': 4 >) ax = plot.figure().add_subplot(111, projection='3d') ax.scatter(point_x, point_y, point_z, color='red') ax.plot_surface(grid_x, grid_y, grid_z, rstride=5, cstride=5, alpha=0.7) plot.show()
if __name__ == '__main__': min_x, min_y = find_minimum() minimum = (min_x, min_y, differentiable_function(min_x, min_y)) draw_chart(minimum, get_grid(0.05))

Заключение
Процесс вычисления минимального значения с помощью алгоритма может быть не очень точным при вычислениях в более крупном масштабе, например, если радиус рабочей плоскости равен 1000, но он очень быстрый по сравнению с точным. Плюс в любом случае, если радиус большой, результат находится примерно в том положении, в котором он должен быть, поэтому разница не будет заметна на графике.
Исходный код:
import numpy as np import matplotlib.pyplot as plot radius = 8 # working plane radius global_epsilon = 0.000000001 # argument increment for derivative centre = (global_epsilon, global_epsilon) # centre of the working circle arr_shape = 100 # number of points processed / 360 step = radius / arr_shape # step between two points def differentiable_function(x, y): return np.sin(x) * np.exp((1 - np.cos(y)) ** 2) + \ np.cos(y) * np.exp((1 - np.sin(x)) ** 2) + (x - y) ** 2 def rotate_vector(length, a): return length * np.cos(a), length * np.sin(a) def derivative_x(epsilon, arg): return (differentiable_function(global_epsilon + epsilon, arg) - differentiable_function(epsilon, arg)) / global_epsilon def derivative_y(epsilon, arg): return (differentiable_function(arg, epsilon + global_epsilon) - differentiable_function(arg, epsilon)) / global_epsilon def calculate_flip_points(): flip_points = np.array([0, 0]) points = np.zeros((360, arr_shape), dtype=bool) cx, cy = centre for i in range(arr_shape): for alpha in range(360): x, y = rotate_vector(step, alpha) x = x * i + cx y = y * i + cy points[alpha][i] = derivative_x(x, y) + derivative_y(y, x) > 0 if not points[alpha][i - 1] and points[alpha][i]: flip_points = np.vstack((flip_points, np.array([alpha, i - 1]))) return flip_points def pick_estimates(positions): vx, vy = rotate_vector(step, positions[1][0]) cx, cy = centre best_x, best_y = cx + vx * positions[1][1], cy + vy * positions[1][1] for index in range(2, len(positions)): vx, vy = rotate_vector(step, positions[index][0]) x, y = cx + vx * positions[index][1], cy + vy * positions[index][1] if differentiable_function(best_x, best_y) > differentiable_function(x, y): best_x = x best_y = y for index in range(360): vx, vy = rotate_vector(step, index) x, y = cx + vx * (arr_shape - 1), cy + vy * (arr_shape - 1) if differentiable_function(best_x, best_y) > differentiable_function(x, y): best_x = x best_y = y return best_x, best_y def gradient_descent(best_estimates, is_x): derivative = derivative_x if is_x else derivative_y best_x, best_y = best_estimates descent_step = step value = derivative(best_y, best_x) while abs(value) > global_epsilon: descent_step *= 0.95 best_y = best_y - descent_step \ if derivative(best_y, best_x) > 0 else best_y + descent_step value = derivative(best_y, best_x) return best_y, best_x def find_minimum(): return gradient_descent(gradient_descent(pick_estimates(calculate_flip_points()), False), True) def get_grid(grid_step): samples = np.arange(-radius, radius, grid_step) x, y = np.meshgrid(samples, samples) return x, y, differentiable_function(x, y) def draw_chart(point, grid): point_x, point_y, point_z = point grid_x, grid_y, grid_z = grid plot.rcParams.update(< 'figure.figsize': (4, 4), 'figure.dpi': 200, 'xtick.labelsize': 4, 'ytick.labelsize': 4 >) ax = plot.figure().add_subplot(111, projection='3d') ax.scatter(point_x, point_y, point_z, color='red') ax.plot_surface(grid_x, grid_y, grid_z, rstride=5, cstride=5, alpha=0.7) plot.show() if __name__ == '__main__': min_x, min_y = find_minimum() minimum = (min_x, min_y, differentiable_function(min_x, min_y)) draw_chart(minimum, get_grid(0.05))
- градиентный спуск
- python
- Нахождение минимума
- Минимум функции
- минимум
- градиент