Rupert  Beatty

Rupert Beatty

1678320840

CSV.swift: CSV reading and writing library written in Swift

CSV.swift 

CSV reading and writing library written in Swift.

Usage for reading CSV

From string

import CSV

let csvString = "1,foo\n2,bar"
let csv = try! CSVReader(string: csvString)
while let row = csv.next() {
    print("\(row)")
}
// => ["1", "foo"]
// => ["2", "bar"]

From file

NOTE: The default character encoding is UTF8.

import Foundation
import CSV

let stream = InputStream(fileAtPath: "/path/to/file.csv")!
let csv = try! CSVReader(stream: stream)
while let row = csv.next() {
    print("\(row)")
}

Getting the header row

import CSV

let csvString = "id,name\n1,foo\n2,bar"
let csv = try! CSVReader(string: csvString,
                         hasHeaderRow: true) // It must be true.

let headerRow = csv.headerRow!
print("\(headerRow)") // => ["id", "name"]

while let row = csv.next() {
    print("\(row)")
}
// => ["1", "foo"]
// => ["2", "bar"]

Get the field value using subscript

import CSV

let csvString = "id,name\n1,foo"
let csv = try! CSVReader(string: csvString,
                         hasHeaderRow: true) // It must be true.

while csv.next() != nil {
    print("\(csv["id"]!)")   // => "1"
    print("\(csv["name"]!)") // => "foo"
}

Provide the character encoding

If you use a file path, you can provide the character encoding to initializer.

import Foundation
import CSV

let stream = InputStream(fileAtPath: "/path/to/file.csv")!
let csv = try! CSVReader(stream: stream,
                         codecType: UTF16.self,
                         endian: .big)

Reading a row into a Decodable object

If you have a destination object that conforms to the Decodable protocol, you can serialize a row with a new instances of the object.

struct DecodableExample: Decodable {
    let intKey: Int
    let stringKey: String
    let optionalStringKey: String?
}

let csv = """
    intKey,stringKey,optionalStringKey
    1234,abcd,
    """

var records = [DecodableExample]()
do {
    let reader = try CSVReader(string: csv, hasHeaderRow: true)
    let decoder = CSVRowDecoder()
    while reader.next() != nil {
        let row = try decoder.decode(DecodableExample.self, from: reader)
        records.append(row)
    }
} catch {
    // Invalid row format
}

Usage for writing CSV

Write to memory and get a CSV String

NOTE: The default character encoding is UTF8.

import Foundation
import CSV

let csv = try! CSVWriter(stream: .toMemory())

// Write a row
try! csv.write(row: ["id", "name"])

// Write fields separately
csv.beginNewRow()
try! csv.write(field: "1")
try! csv.write(field: "foo")
csv.beginNewRow()
try! csv.write(field: "2")
try! csv.write(field: "bar")

csv.stream.close()

// Get a String
let csvData = csv.stream.property(forKey: .dataWrittenToMemoryStreamKey) as! Data
let csvString = String(data: csvData, encoding: .utf8)!
print(csvString)
// => "id,name\n1,foo\n2,bar"

Write to file

NOTE: The default character encoding is UTF8.

import Foundation
import CSV

let stream = OutputStream(toFileAtPath: "/path/to/file.csv", append: false)!
let csv = try! CSVWriter(stream: stream)

try! csv.write(row: ["id", "name"])
try! csv.write(row: ["1", "foo"])
try! csv.write(row: ["1", "bar"])

csv.stream.close()

Installation

CocoaPods

pod 'CSV.swift', '~> 2.4.3'

Carthage

github "yaslab/CSV.swift" ~> 2.4.3

Swift Package Manager

.package(url: "https://github.com/yaslab/CSV.swift.git", .upToNextMinor(from: "2.4.3"))

Reference specification


Download Details:

Author: Yaslab
Source Code: https://github.com/yaslab/CSV.swift 
License: MIT license

#swift #csv #writing #reading 

CSV.swift: CSV reading and writing library written in Swift
Bongani  Ngema

Bongani Ngema

1672054680

How to Build a Reading List with Svelte

Svelte has received a lot of praise over the last two years and is far from being “just another frontend framework”. It won “breakthrough of the year” on the State of JS survey 2019, followed by topping the satisfaction rating in 2020. It was also voted the most loved web framework in the Stack Overflow 2021 survey.

Svelte appeals to developers with its combination of a small bundle size, very good performance, and ease of use. At the same time, it comes packed with a lot of goodies. A simple state management solution to build upon is already provided, as well as ready-to-use transitions and animations. This introductory tutorial will shed light on how does Svelte achieves this. The following tutorials in the series will go into more detail on how to implement applications with Svelte using the various possibilities Svelte provides.

The Svelte Backstory

But first, a little back story on Svelte. Though it only entered the mainstream in the early 2020s, Svelte has been around for much longer.

The first commit to GitHub was in late 2016. Its creator is Rich Harris, an open-source wizard whose most prominent other invention is Rollup, a modern bundler. Rich Harris worked at the news magazine The Guardian as a graphics editor at the time. His daily routine was to create interactive visualizations for the website, and he wanted to have a tool that easily let him write these without compromising on bundle size or speed. At the same time, he wanted something approachable so other less tech-savvy colleagues would be able to create visualizations fast.

Out of these needs, Svelte was born. Starting from the news room, Svelte quickly gathered a small following in the open-source community. But it wasn’t until April 2019 where Svelte really got known to the world. This date marked the release of version 3, which was a complete rewrite with a focus on developer experience and approachability. Since then, Svelte’s popularity has risen a lot, more maintainers have joined the team, and Rich Harris has even joined Vercel to work on Svelte full-time.

For an in-depth tutorials into Svelte, and it’s differences to React and Vue, check out Svelte 3: A Radical Compiler-based JavaScript Framework.

Building a Simple Book List

Let’s dive into Svelte! We’ll build a small book list that allows us to add and remove books from our reading list. The final result will look something like the image below.

Final App

We’ll start by scaffolding our project from a project template. We’ll use the official Svelte template. Alternatives would be to use a Vite-powered template or to use SvelteKit, a framework on top of Svelte for building full-fledged apps with built-in routing—but we’ll keep it as barebones as possible for this tutorial.

After downloading the template, switch to its folder and run npm install, which downloads all packages we need to get going. Then we’ll switch to App.svelte, where we’ll replace the contents with an HTML-only version to lay out the visuals we want:

<h4>Add Book</h4>
<input type="text" />
<h4>My Books</h4>
<ul>
  <li>A book</li>
</ul>

We can write the above code directly at the top level of the Svelte file; we don’t need to add any wrapper elements. Svelte’s syntax is a superset of HTML, so anything that is valid inside an HTML file is valid inside a Svelte file.

The question now is how to get the dynamic parts in there. We’ll start by adding a static list to the script and render that through a loop:

<script>
  let books = ['Learning Svelte', 'The Zen of Cooking Tea'];
</script>

<label>
  <h4>Add Book</h4>
  <input type="text" />
</label>
<h4>My Books</h4>
<ul>
  {#each books as book}
    <li>{book}</li>
  {/each}
</ul>

We added a script tag in which we put our JavaScript logic related to the component. That logic is executed each time the component mounts. We also enhance the HTML with special Svelte syntax to create a loop and print the title of each book. As you can see, Svelte has distinct syntax for control flow blocks, unlike Vue or Angular, which add such functionality in the form of special attributes. This makes the code more readable, as you can more easily spot it. It also makes it unnecessary to create wrapper elements if you want to contain more than one top-level item within the control flow block.

The title of a book is outputted by surrounding the variable with curly braces. In general, whenever you encounter a curly brace within the template, you know you are entering something Svelte-related. We’ll look into the template syntax in more detail in Part 2 of this tutorial series.

Reacting to User Input

We can now render an arbitrary list of book titles, defined by our books variable. What about adding a new book? To do this, we need to enhance our logic in the <script> tag and connect it to the <input> element:

<script>
  let books = ['Learning Svelte', 'The Zen of Cooking Tea'];
  let newBook = '';

  function addBook(evt) {
    if (evt.key === 'Enter') {
      books = [...books, newBook];
      newBook = '';
    }
  }
</script>

<label>
  <h4>Add Book</h4>
  <input type="text" bind:value={newBook} on:keydown={addBook} />
</label>
<h4>My Books</h4>
<ul>
  {#each books as book}
    <li>{book}</li>
  {/each}
</ul>

We added a new variable called newBook, which should mirror the input value. To do that, we bind it to the <input> by writing bind:value={newBook}. This establishes a two-way binding, so every time the user enters text into the <input>, newBook updates, and if newBook is updated in the <script> tag, the display value of <input> changes. We could have done the same with simple dynamic attributes, but this way saves us some code—a thought pattern you’ll come across often in Svelte.

When the user presses enter, we want to add the new book title to the list. To do this, we add a DOM event listener. To tell Svelte to hook into the event, we just add a colon between on and the rest of the event name—so in this case it’s on:keydown. After that, we use the curly braces and place the name of the function inside. The function is called each time the event fires off. More on this template syntax can be found in Part 2 of this tutorial series.

The function to call in this case is addBook, in which we check the keyboard event, and if the user indeed pressed enter, we update the books variable. Notice the lack of a this context like we find in Angular or Vue 2, or the lack of special value objects like in Vue 3, or the lack of setState in React. Svelte doesn’t need extra syntax in this case to know that the variable has updated. This might feel like magic, but also like “just plain simple JavaScript” at the same time.

To understand how Svelte achieves this, we need to look under the hood. What does Svelte actually do with a .svelte file, and when does it process it? The answer: Svelte is actually a compiler! It does most of the work before your code is even loaded in the browser. Svelte parses the code and transforms it into regular JavaScript. During parsing, it’s able to see that variables like newBook are used in the template, so assignments to it will cause rerenders. The compilation output will therefore wrap these assignments with calls to a $$invalidate function, which will schedule a rerender of this exact component for the next browser paint. This is the secret to Svelte’s great performance: it knows in advance which parts could trigger rerenders and then only needs to do work in these exact places, surgically updating the DOM. It’s also the reason why the bundle sizes of Svelte applications are so small: everything that’s not needed just won’t be part of the output, so Svelte can leave out every part of its tiny runtime that isn’t needed. A Svelte Hello World! app has a bundle size of just 2.5KB!

The only thing to watch out for is that Svelte does only look for assignments. That’s why we need to do books = [...books, newBook]; or books.push(newBook); books = books;. Otherwise, Svelte wouldn’t know that books has updated.

Compilation Output

Finishing Touches

We did it! We can now view and add books to our list! It doesn’t look that pretty, though, so let’s put some finishing touches to our UI. First, we’ll add some CSS to style our elements:

<!-- script and html code... -->

<style>
  input {
    padding: 5px 10px;
  }
  li {
    list-style: none;
  }
  ul {
    padding: 5px 0;
  }
</style>

As you can see, we just add a <style> tag to our .svelte file and continue to write regular CSS in it. If you’re fearing that the code above will style all <input>, <li> or <ul> tags in the entire application, be assured that it won’t. Svelte scopes styles by default, so they only apply to the component they’re defined in. If you want to define something globally, wrap the selector with the :global function. If, for example, you’d like to style all <input>s in the application, the code would be :global(input) { padding: 5px 10px; }.

The styling is better now. Let’s finish it off with a transition for better UX: we want new list elements to fade in. To do that, we just need to reach for one of Svelte’s built-in transitions and animations and apply them:

<script>
  import { fade } from 'svelte/transition';
  // ..
</script>

<!-- input ... -->
<h4>My Books</h4>
<ul>
  {#each books as book}
    <li transition:fade>{book}</li>
  {/each}
</ul>

<!-- styling ... -->

And that’s it! Just by importing one of the built-in transitions and applying it by adding transition:fade to the element, we get that smooth fade-in transition. Our mini app is now finished. This doesn’t contain the topbar and the background gradient yet, but it should be easy now for you to add this as well. This is the end result:

<script>
  import { fade } from 'svelte/transition';

  let books = ['Learning Svelte', 'The Zen of Cooking Tea'];
  let newBook = '';

  function addBook(evt) {
    if (evt.key === 'Enter') {
      books = [...books, newBook];
      newBook = '';
    }
  }
</script>

<label>
  <h4>Add Book</h4>
  <input type="text" bind:value={newBook} on:keydown={addBook} />
</label>
<h4>My Books</h4>
<ul>
  {#each books as book}
    <li transition:fade>{book}</li>
  {/each}
</ul>

<style>
  input {
    padding: 5px 10px;
  }
  li {
    list-style: none;
  }
  ul {
    padding: 5px 0;
  }
</style>

Architectural Considerations

We’ve seen how to write a little app in Svelte with just 32 lines of code. We’ve only scratched the surface, of course. A full-blown app needs some kind of state management, multiple components, and ways to integrate these components with each other.

For example, it would make sense to split out the display of one to-do item into a separate component, as we’ll add features like editing the name in-place or marking it as done. Having this all in one component would become hard to maintain over time. Luckily, using other components is as easy as importing it as a default import from another Svelte file and interacting with it in a similar way to what we’ve already seen with regular DOM elements. We’ll look into component interaction in more detail in Part 5 of this series.

Another example would be the management of to-dos. Right now, they’re handled inside the component and there’s no connection to a backend. If we were to add API calls, we would mix UI logic with backend interaction, which is generally better handled outside of components for better separation of concerns. We can use Svelte stores for this, which we’ll look at in Part 4.

As you can see, Svelte has solutions to all of our requirements, and we’ll look at them over the course of this series.

Ready, Set … Svelte?

So, is it safe to use Svelte for your next project? Your manager might ask if Svelte will be around in the years to come or burn out like previous frontend framework stars. There isn’t one big company backing Svelte’s entire development as there is for Angular and React, but Vue has already shown that this isn’t a problem. Moreover, as stated at the beginning, Rich Harris, the creator of Svelte, is now working on it full-time. With Svelte’s continuous rise in popularity, there’s no sign of it going anywhere in the years to come.

Another aspect of choosing a framework is the ecosystem and its tooling. The ecosystem is still small compared to React, but new libraries are coming out every day, and there are already a handful of very good component libraries. At the same time, since Svelte is so close to vanilla HTML and JavaScript, it’s very easy to integrate any existing regular HTML/JavaScript library into your codebase, with no need for wrapper libraries.

Regarding tooling, Svelte is looking pretty good. There’s an official VS Code extension that’s actively maintained, as well as an underlying language server that can be used by many other IDEs to integrate Intellisense. IntelliJ also has a plugin for Svelte and recently hired the creator behind it to work at JetBrains. There are also various tools for integrating Svelte with various bundlers. And yes, you can also use TypeScript with Svelte.

If you’re looking to build a full-blown website or web app, you might also be interested in checking out SvelteKit (See our Beginner’s Guide to SvelteKit). It provides a stellar development experience and comes with a flexible filesystem-based router. It also enables you to deploy to many different platforms like Vercel, Netlify, your own Node server, or just a good old static file server, depending on the features and needs of your application.

Quick Facts on Svelte

In brief, here are the important points to remember about Svelte:

  • it has a full-time maintainer
  • it has good tooling
  • its features are stable
  • its ecosystem is growing
  • SvelteKit is available for building apps fast

Next Steps

Svelte is definitely ready to use for your next project!

This was the first part of 6 part series on SitePoint Premium. In Part 2, we’ll take a close look at the template syntax. In Part 3, we’ll look at reactive statements and how they help us react to variable changes or derive computed variables. Part 4 will look at stores, which will help us with logic outside and across Svelte files, and which we can also use for state management. Part 5 looks at various component interaction concepts. Finally, in Part 6, we’ll look into testing Svelte apps.

This series is also available on Amazon here: Svelte: A Beginner’s Guide.

We hope to have sparked your interest in Svelte!

Original article source at: https://www.sitepoint.com/

#svelte #reading 

How to Build a Reading List with Svelte
Elian  Harber

Elian Harber

1665485400

Gopher-reading-list: A Curated Selection Of Blog Posts on Go

Gopher Reading List

中文版

Here is a reading list of blog posts about Go. It aspires to include only the most useful and relevant material that anyone writing Go should eventually read. By definition, the list is a work in progress.

Rather than being comprehensive, the list is a curated selection fixed at 200 entries.

Go is growing fast and so are the number of blog posts about it. If an interested reader knows of a great post not on this list, please open an issue with a link to the post. Not every blog post linked in an issue will make its way into the list. Nonetheless, the issue list (both open and closed) is a good source of additional reading material.

NOTE: Any new additions will need to replace something else on the list to keep it at a fixed length.

Start Here

See Go Books for a list of books, both free and paid.

Beginner

Some basics

Worth reading, again and again

Organization and Style

Web

Concurrency

Go Modules

Intermediate

Code Design

Concurrency

Testing

Web

Tools

Trivia

Diagnostics

Language

Miscellaneous

Advanced

Low Level Concerns

Performance

Garbage Collection

Concurrency

JSON Encoding and Decoding

Miscellaneous

Download Details:

Author: Enocom
Source Code: https://github.com/enocom/gopher-reading-list 
License: Apache-2.0 license

#go #golang #reading 

Gopher-reading-list: A Curated Selection Of Blog Posts on Go

Regresión logística usando PyTorch en Python

Cómo realizar el algoritmo de regresión logística en Python con PyTorch

Aprenda a realizar un algoritmo de regresión logística utilizando el marco de aprendizaje profundo de PyTorch en un conjunto de datos de ejemplo de abandono de clientes en Python.

La regresión logística es un modelo probabilístico que se utiliza para describir la probabilidad de resultados discretos dadas las variables de entrada. A pesar del nombre, la regresión logística es un modelo de clasificación, no un modelo de regresión. En pocas palabras, la regresión logística es similar a la regresión lineal excepto por la categorización.

Calcula la probabilidad del resultado utilizando la función sigmoidea. Debido a la transformación no lineal de la variable de entrada, la regresión logística no necesita correlaciones lineales entre las variables de entrada y salida.

Este tutorial se enfoca en desarrollar un modelo de regresión logística para pronosticar el desgaste de clientes en PyTorch.

¿Qué es una regresión logística?

Aunque el nombre del método incluye el término "regresión", es esencialmente una técnica de aprendizaje automático supervisado diseñada para manejar problemas de clasificación. Se debe al uso del algoritmo de la función logística, que varía de 0 a 1.

Como resultado, podemos usar la regresión logística para pronosticar la probabilidad de que una sola variable característica (X) pertenezca a una categoría específica (Y).

La regresión logística tiene muchas similitudes con la regresión lineal, aunque la regresión lineal se usa para predecir valores numéricos en lugar de problemas de clasificación. Ambas estrategias usan una línea para representar la variable objetivo.

La regresión lineal ajusta una línea a los datos para predecir una nueva cantidad, mientras que la regresión logística ajusta una línea para separar las dos clases de manera óptima.

La función sigmoidea

Para comprender qué es la regresión logística y cómo funciona, primero debe comprender la función sigmoidea y la función logaritmo natural.

Esta imagen muestra la curva en forma de S de una variable para valores de x que van de 0 a 1:

función sigmoidea

Fuente: DepositPhotos

A lo largo de la mayor parte de su dominio, la función sigmoidea tiene valores extremadamente cercanos a 0 o 1. Debido a esto, es apropiada para su uso en problemas de clasificación binaria. Eche un vistazo al siguiente gráfico para ver cómo se representa el modelo de regresión logística:

Modelo de regresión logística

 

Fuente: hacker de datos

Como puede ver, comenzaremos calculando la salida de una función lineal, z. La función sigmoidea tomará esta salida z como entrada. A continuación, para calcular z, generaremos la predicción ŷ, que determinará z. Si z es un número positivo significativo, entonces ŷ estará cerca de uno.

Por otro lado, si z tiene un número significativamente negativo, ŷ estará cerca de cero.

En consecuencia, ŷ siempre estará en el rango de 0 a 1. Usar un valor de umbral de 0,5 es una técnica sencilla para categorizar la predicción ŷ.

Si nuestro pronóstico es más extenso que 0.5, asumimos que ŷ es igual a 1.

En caso contrario, supondremos que ŷ es igual a cero. Cuando aplicamos la regresión logística, nuestro objetivo es intentar calcular los parámetros w y b para que ŷ se convierta en una estimación decente de la probabilidad de ŷ=1.

Descripción de datos

El conjunto de datos utilizado en este estudio proporciona información sobre la deserción de clientes en función de diversas variables. El conjunto de datos tiene 2000 filas y 15 características que pueden usarse para predecir la rotación. Se puede descargar  aquí .

Relacionado: Predicción de abandono de clientes: una guía completa en Python .

Instalemos las dependencias de este tutorial:

$ pip install matplotlib numpy pandas scikit_learn==1.0.2 torch==1.10.1

Para descargar automáticamente el conjunto de datos, podemos usar gdown:

$ pip install --upgrade gdown
$ gdown --id 12vfq3DYFId3bsXuNj_PhsACMzrLTfObs

Empecemos:

import pandas as pd
import numpy as np
import torch
import torch.nn as nn
from sklearn.utils import resample
from sklearn import preprocessing
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from warnings import filterwarnings
filterwarnings('ignore')

Leamos los datos, suelte las columnas , , y miremos la forma de los yeardatos  customer_idphone_no

#reading data
data = pd.read_csv("data_regression.csv")
##The dimension of the data is seen, and the output column is checked to see whether it is continuous or discrete. 
##In this case, the output is discrete, so a classification algorithm should be applied.
data = data.drop(["year", "customer_id", "phone_no"], axis=1)
print(data.shape)         # Lookiing the shape of the data
print(data.columns)       # Looking how many columns data has
data.dtypes  
data.head()

Tratamiento de valor nulo

Si tenemos valores nulos, debemos trabajar en eso antes de enviarlo a nuestro modelo:

data.isnull().sum()
gender                    24
age                        0
no_of_days_subscribed      0
multi_screen               0
mail_subscribed            0
weekly_mins_watched        0
minimum_daily_mins         0
maximum_daily_mins         0
weekly_max_night_mins      0
videos_watched             0
maximum_days_inactive     28
customer_support_calls     0
churn                     35
dtype: int64
final_data = data.dropna()         # Dropping the null values

Muestreo de datos

Probemos los datos ya que nuestros datos están muy desequilibrados. Se han utilizado Upsampling y Downsampling para clases minoritarias y mayoritarias de la salida. Finalmente, los datos se concatenan para su posterior procesamiento:

final_data["churn"].value_counts()       
# let us see how many data is there in each class for deciding the sampling data number
0.0    1665
1.0     253
Name: churn, dtype: int64

División de datos

Primero, necesitamos dividir el marco de datos en clases separadas antes de muestrear:

data_majority = final_data[final_data['churn']==0] #class 0
data_minority = final_data[final_data['churn']==1] #class 1
#upsampling minority class
data_minority_upsampled = resample(data_minority, replace=True, n_samples=900, random_state=123) 
 #downsampling majority class
data_majority_downsampled = resample(data_majority, replace=False, n_samples=900, random_state=123)
#concanating both upsampled and downsampled class
## Data Concatenation:  Concatenating the dataframe after upsampling and downsampling 
#concanating both upsampled and downsampled class
data2 = pd.concat([data_majority_downsampled, data_minority_upsampled])
## Encoding Catagoricals:  We need to encode the categorical variables before feeding it to the model
data2[['gender', 'multi_screen', 'mail_subscribed']]
#label encoding categorical variables
label_encoder = preprocessing.LabelEncoder()
data2['gender']= label_encoder.fit_transform(data2['gender'])
data2['multi_screen']= label_encoder.fit_transform(data2['multi_screen'])
data2['mail_subscribed']= label_encoder.fit_transform(data2['mail_subscribed'])
## Lets now check again the distribution of the oputut class after sampling
data2["churn"].value_counts()
0.0    900
1.0    900
Name: churn, dtype: int64

En el siguiente código, nos ocupamos del escalado, la división de los conjuntos de entrenamiento y prueba, y la separación de variables dependientes e independientes:

#indenpendent variable 
X = data2.iloc[:,:-1]
## This X will be fed to the model to learn params 
#scaling the data
sc = StandardScaler()         # Bringing the mean to 0 and variance to 1, so as to have a non-noisy optimization
X = sc.fit_transform(X)
X = sc.transform(X)
## Keeping the output column in a separate dataframe
data2 = data2.sample(frac=1).reset_index(drop=True) ## Shuffle the data frame and reset index
n_samples, n_features = X.shape ## n_samples is the number of samples and n_features is the number of features
#output column
Y = data2["churn"]
#output column
Y = data2["churn"]
##Data Splitting: 
## The data is processed, so now we can split the data into train and test to train the model with training data and test it later from testing data.
#splitting data into train and test
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.30, random_state=42, stratify = Y)
print((y_train == 1).sum())
print((y_train == 0).sum())
630
630

Imprimimos el tipo de tren y conjunto de prueba:

print(type(X_train))
print(type(X_test))
print(type(y_train.values))
print(type(y_test.values))
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>

Convirtiéndolos a tensores como funciona PyTorch, usaremos el torch.from_numpy()método:

X_train = torch.from_numpy(X_train.astype(np.float32))
X_test = torch.from_numpy(X_test.astype(np.float32))
y_train = torch.from_numpy(y_train.values.astype(np.float32))
y_test = torch.from_numpy(y_test.values.astype(np.float32))

Al hacer que el vector de salida Y sea un vector de columna para multiplicaciones de matrices, realizamos este cambio usando la operación de vista como se muestra en el siguiente código:

y_train = y_train.view(y_train.shape[0], 1)
y_test = y_test.view(y_test.shape[0], 1)

Función de activación

Usamos funciones de activación para representar la interacción dinámica en datos lineales. Aquí, utilizamos una función de activación sigmoidea. Elegimos la función sigmoidea porque limitará el valor de 0 a 1. Las funciones de activación ayudan a introducir la no linealidad en la salida de una neurona, lo que mejora la precisión, la eficiencia informática y la velocidad de convergencia.

Las funciones de activación deben ser diferenciables y convergentes rápidamente con respecto a los pesos.

Desventajas sigmoideas:

  • Gradiente de fuga durante la etapa de retropropagación de una red neuronal (especialmente RNN).
  • Debido a su naturaleza exponencial, es computacionalmente costoso.

Para citar de este documento : "La principal función de activación que se utilizó ampliamente es la función sigmoidea. Sin embargo, cuando se introdujo la Unidad lineal rectificadora (ReLU) (Nair & Hinton, 2010), pronto se convirtió en un mejor reemplazo para la función Sigmoid debido a su impacto positivo en las diferentes tareas de aprendizaje automático. Si bien usar Sigmoid y trabajar en capas menos profundas no presenta ningún problema, surgen algunos problemas cuando la arquitectura se vuelve más profunda porque los términos derivados que son menores que 1 se multiplicarán entre sí muchas veces y los valores se volverán más pequeños. y más pequeño hasta que el gradiente tiende a cero y por lo tanto desaparece. Por otro lado, si los valores son mayores que uno, sucede lo contrario, los números se multiplican y se hacen más y más grandes hasta que tienden al infinito y explotan el gradiente. Una buena solución sería mantener los valores en 1 para que, incluso cuando se multipliquen, no cambien. Esto es exactamente lo que hace ReLU: tiene gradiente 1 para entradas positivas y 0 para entradas negativas.

Construcción de modelos: creación de un modelo de regresión logística en Pytorch

A continuación se muestra el código responsable de construir el modelo de regresión logística en PyTorch:

#logistic regression class
class LogisticRegression(nn.Module):
    def __init__(self, n_input_features):
        super(LogisticRegression, self).__init__()
        self.linear = nn.Linear(n_input_features, 1)
    
    #sigmoid transformation of the input 
    def forward(self, x):
        y_pred = torch.sigmoid(self.linear(x))
        return y_pred

Debe declarar las capas en su modelo en el __init__()método. Hemos utilizado capas lineales, que se especifican mediante el torch.nnmódulo. La capa puede recibir cualquier nombre, como self.linear(en nuestro caso). He declarado una capa lineal porque eso es regresión logística.

La sintaxis es: torch.nn.Linear(in_features, out_features, bias=True)

El forward()método está a cargo de realizar el pase hacia adelante/propagación. La entrada se enruta a través de la capa previamente establecida, y la salida de esa capa se envía a través de la función de activación sigmoidea.

Inicializamos el modelo:

lr = LogisticRegression(n_features)

Compilación de modelos

Definamos el número de épocas y la tasa de aprendizaje que queremos que nuestro modelo entrene. Como los datos son binarios, usaremos Binary Cross Entropy como la función de pérdida utilizada para optimizar el modelo usando un optimizador SGD.

Pérdida BCE

También utilizaremos la función de pérdida (error) L para evaluar el rendimiento de nuestro algoritmo. Recuerde que la función de pérdida solo se aplica a una muestra de entrenamiento, y la función de pérdida más empleada generalmente es un error cuadrático. Sin embargo, la función de pérdida de error al cuadrado en la regresión logística no es la mejor opción. Produce un problema de optimización que no es convexo, y el enfoque de descenso de gradiente puede no converger de manera óptima. Aplicaremos la pérdida BCE para alcanzar el óptimo global.

La pérdida BCE significa pérdida de entropía cruzada binaria y se usa a menudo en instancias de clasificación binaria.

Vale la pena señalar que cuando se usa la función de pérdida BCE , la salida del nodo debe estar entre (0–1). Para esto, necesitaremos emplear un optimizador apropiado. Elegimos SGD, o Stochastic Gradient Descent, un optimizador de uso regular. Otros optimizadores incluyen a Adam, Lars y otros.

Debe proporcionar los parámetros del modelo y la tasa de aprendizaje como entrada para el optimizador. SGD elige un punto de datos al azar de todo el conjunto de datos en cada iteración para minimizar los cálculos drásticamente.

También es habitual muestrear una pequeña cantidad de puntos de datos en lugar de solo uno en cada paso (a menudo denominado descenso de gradiente de mini lotes). Los mini lotes intentan equilibrar la eficiencia del descenso de gradiente y la velocidad de SGD.

Tasa de aprendizaje

La tasa de aprendizaje es un hiperparámetro ajustable que se utiliza en el entrenamiento de redes neuronales con un poco de valor positivo, a menudo de 0,0 a 0,1. Las tasas de aprendizaje más bajas necesitan más épocas de entrenamiento debido a los cambios menores en los pesos con cada actualización, mientras que las tasas de aprendizaje más excelentes producen cambios rápidos y requieren menos épocas de entrenamiento.

Una tasa de aprendizaje alta puede hacer que el modelo converja demasiado rápido en una solución deficiente, mientras que una tasa de aprendizaje baja puede hacer que el proceso se detenga. Usando los programas de tasa de aprendizaje, puede cambiar la tasa de aprendizaje a medida que avanza el entrenamiento.

Ajusta la tasa de aprendizaje en función de un programa predefinido, como basado en el tiempo, basado en pasos o exponencial.

Podemos crear un cronograma de tasa de aprendizaje para actualizar la tasa de aprendizaje a lo largo del entrenamiento en función de una regla predefinida. El planificador de tasa de aprendizaje más común es un decaimiento de pasos, que reduce la tasa de aprendizaje en un cierto porcentaje después de un cierto número de épocas de entrenamiento. Finalmente, podemos decir que un cronograma de tasa de aprendizaje es una estructura predeterminada para ajustar la tasa de aprendizaje entre épocas o iteraciones a medida que ocurre el entrenamiento.

Las siguientes son dos de las estrategias más prevalentes para el aprendizaje de horarios de tasas:

  • Decaimiento de la tasa de aprendizaje: ocurre cuando elegimos una tasa de aprendizaje inicial y luego la bajamos progresivamente de acuerdo con un programador.
  • Tasa de aprendizaje constante : Como su nombre lo indica, establecemos una tasa de aprendizaje y no la variamos a lo largo del entrenamiento.

Nota: La tasa de aprendizaje es un hiperparámetro que debe modificarse. En lugar de usar una tasa de aprendizaje constante, podríamos comenzar con un valor más alto de LR y luego disminuirlo gradualmente después de un número específico de rondas. Nos permite tener una convergencia más rápida al principio mientras se reducen los riesgos de sobrepasar la pérdida.

En PyTorch, podemos utilizar múltiples programadores del optimpaquete. Puede seguir este enlace para ver cómo puede ajustar la tasa de aprendizaje de una red neuronal usando PyTorch. Definamos los parámetros, la pérdida y el optimizador:

num_epochs = 500                                        
# Traning the model for large number of epochs to see better results  
learning_rate = 0.0001                               
criterion = nn.BCELoss()                                
# We are working on lgistic regression so using Binary Cross Entropy
optimizer = torch.optim.SGD(lr.parameters(), lr=learning_rate)      
# Using ADAM optimizer to find local minima   

Seguimiento del proceso de formación:

for epoch in range(num_epochs):
    y_pred = lr(X_train)
    loss = criterion(y_pred, y_train)             
    loss.backward()
    optimizer.step()
    optimizer.zero_grad()
    if (epoch+1) % 20 == 0:                                         
        # printing loss values on every 10 epochs to keep track
        print(f'epoch: {epoch+1}, loss = {loss.item():.4f}')
epoch: 20, loss = 0.8447
epoch: 40, loss = 0.8379
epoch: 60, loss = 0.8316
epoch: 80, loss = 0.8257
epoch: 100, loss = 0.8203
epoch: 120, loss = 0.8152
epoch: 140, loss = 0.8106
epoch: 160, loss = 0.8063
epoch: 180, loss = 0.8023
epoch: 200, loss = 0.7986
epoch: 220, loss = 0.7952
epoch: 240, loss = 0.7920
epoch: 260, loss = 0.7891
epoch: 280, loss = 0.7863
epoch: 300, loss = 0.7838
epoch: 320, loss = 0.7815
epoch: 340, loss = 0.7793
epoch: 360, loss = 0.7773
epoch: 380, loss = 0.7755
epoch: 400, loss = 0.7737
epoch: 420, loss = 0.7721
epoch: 440, loss = 0.7706
epoch: 460, loss = 0.7692
epoch: 480, loss = 0.7679
epoch: 500, loss = 0.7667

Aquí, ocurre el primer pase hacia adelante. A continuación, se calcula la pérdida. Cuando loss.backward()se llama, calcula el gradiente de pérdida con respecto a los pesos (de la capa). Luego, los pesos se actualizan llamando a optimizer.step(). Después de esto, los pesos deben vaciarse para la siguiente iteración. Entonces zero_grad()se llama el método.

El código anterior imprime la pérdida en cada vigésima época.

Rendimiento del modelo

Veamos finalmente la precisión del modelo:

with torch.no_grad():
    y_predicted = lr(X_test)
    y_predicted_cls = y_predicted.round()
    acc = y_predicted_cls.eq(y_test).sum() / float(y_test.shape[0])
    print(f'accuracy: {acc.item():.4f}')
accuracy: 0.5093

Tenemos que usar torch.no_grad()aquí. El objetivo es omitir el cálculo del gradiente sobre los pesos. Por lo tanto, cualquier cosa que coloque dentro de este bucle no modificará los pesos y, por lo tanto, no interrumpirá el proceso de retropropagación.

También podemos ver la precisión, el recuerdo y la puntuación F1 utilizando el informe de clasificación:

#classification report
from sklearn.metrics import classification_report
print(classification_report(y_test, y_predicted_cls))
              precision    recall  f1-score   support

         0.0       0.51      0.62      0.56       270
         1.0       0.51      0.40      0.45       270

    accuracy                           0.51       540
   macro avg       0.51      0.51      0.50       540
weighted avg       0.51      0.51      0.50       540

Visualizando la Matriz de Confusión:

#confusion matrix
from sklearn.metrics import confusion_matrix
confusion_matrix = confusion_matrix(y_test, y_predicted_cls)
print(confusion_matrix)
[[168 102]
 [163 107]]

Conclusión

Tenga en cuenta que nuestro modelo no funciona bien en este conjunto de datos relativamente complejo. El objetivo de este tutorial es mostrarle cómo hacer una regresión logística en PyTorch. Si desea obtener una mayor precisión y otras métricas, considere ajustar los hiperparámetros de entrenamiento, como aumentar la cantidad de épocas y la tasa de aprendizaje o incluso agregar una capa más (es decir, una red neuronal).

Obtén el código completo en este cuaderno de Colab .

Fuente https://www.thepythoncode.com

#python #pytorch 

Regresión logística usando PyTorch en Python

Comment effectuer un algorithme de régression logistique en Python avec PyTorch

Régression logistique utilisant PyTorch en Python

Découvrez comment exécuter un algorithme de régression logistique à l'aide du cadre d'apprentissage en profondeur PyTorch sur un exemple d'ensemble de données de désabonnement client en Python.

La régression logistique est un modèle probabiliste utilisé pour décrire la probabilité de résultats discrets compte tenu des variables d'entrée. Malgré son nom, la régression logistique est un modèle de classification, pas un modèle de régression. En un mot, la régression logistique est similaire à la régression linéaire, à l'exception de la catégorisation.

Il calcule la probabilité du résultat à l'aide de la fonction sigmoïde. En raison de la transformation non linéaire de la variable d'entrée, la régression logistique n'a pas besoin de corrélations linéaires entre les variables d'entrée et de sortie.

Ce didacticiel se concentre sur le développement d'un modèle de régression logistique pour prévoir l'attrition des clients dans PyTorch.

Qu'est-ce qu'une régression logistique

Bien que le nom de la méthode inclue le terme "régression", il s'agit essentiellement d'une technique d'apprentissage automatique supervisée conçue pour gérer les problèmes de classification. Cela est dû à l'utilisation par l'algorithme de la fonction logistique, qui varie de 0 à 1.

Par conséquent, nous pouvons utiliser la régression logistique pour prévoir la probabilité qu'une seule variable caractéristique (X) appartienne à une catégorie spécifique (Y).

La régression logistique présente de nombreuses similitudes avec la régression linéaire, bien que la régression linéaire soit utilisée pour prédire des valeurs numériques plutôt que des problèmes de classification. Les deux stratégies utilisent une ligne pour représenter la variable cible.

La régression linéaire ajuste une ligne aux données pour prédire une nouvelle quantité, tandis que la régression logistique ajuste une ligne pour séparer les deux classes de manière optimale.

La fonction sigmoïde

Pour comprendre ce qu'est la régression logistique et comment elle fonctionne, vous devez d'abord comprendre la fonction sigmoïde et la fonction logarithme naturel.

Cette image représente la courbe en forme de S d'une variable pour des valeurs de x comprises entre 0 et 1 :

Fonction sigmoïde

Source : DépôtPhotos

Dans la majeure partie de son domaine, la fonction sigmoïde a des valeurs extrêmement proches de 0 ou 1. Pour cette raison, elle est appropriée pour une utilisation dans les problèmes de classification binaire. Jetez un œil au graphique ci-dessous pour voir comment le modèle de régression logistique est représenté :

Modèle de régression logistique

 

Source : hacker de données

Comme vous pouvez le voir, nous allons commencer par calculer la sortie d'une fonction linéaire, z. La fonction sigmoïde prendra cette sortie z en entrée. Ensuite, pour z calculé, nous allons générer la prédiction ŷ, que z déterminera. Si z est un nombre positif significatif, alors ŷ sera proche de un.

D'autre part, si z a un nombre significativement négatif, ŷ sera proche de zéro.

Par conséquent, ŷ sera toujours compris entre 0 et 1. L'utilisation d'une valeur seuil de 0,5 est une technique simple pour catégoriser la prédiction ŷ.

Si notre prévision est plus étendue que 0,5, nous supposons que ŷ est égal à 1.

Sinon, nous supposerons que ŷ est égal à zéro. Lorsque nous appliquons la régression logistique, notre objectif est d'essayer de calculer les paramètres w et b de sorte que ŷ devienne une estimation décente de la vraisemblance de ŷ=1.

Description des données

L'ensemble de données utilisé dans cette étude fournit des informations sur l'attrition de la clientèle en fonction de diverses variables. L'ensemble de données contient 2 000 lignes et 15 entités qui peuvent être utilisées pour prédire l'attrition. Il peut être téléchargé  ici .

Connexe : Customer Churn Prediction : Un guide complet en Python .

Installons les dépendances de ce tutoriel :

$ pip install matplotlib numpy pandas scikit_learn==1.0.2 torch==1.10.1

Pour télécharger automatiquement le jeu de données, nous pouvons utiliser gdown:

$ pip install --upgrade gdown
$ gdown --id 12vfq3DYFId3bsXuNj_PhsACMzrLTfObs

Commençons:

import pandas as pd
import numpy as np
import torch
import torch.nn as nn
from sklearn.utils import resample
from sklearn import preprocessing
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from warnings import filterwarnings
filterwarnings('ignore')

Lisons les données, supprimons les colonnes yearcustomer_idphone_noet examinons la forme des données :

#reading data
data = pd.read_csv("data_regression.csv")
##The dimension of the data is seen, and the output column is checked to see whether it is continuous or discrete. 
##In this case, the output is discrete, so a classification algorithm should be applied.
data = data.drop(["year", "customer_id", "phone_no"], axis=1)
print(data.shape)         # Lookiing the shape of the data
print(data.columns)       # Looking how many columns data has
data.dtypes  
data.head()

Traitement de la valeur nulle

Si nous avons des valeurs nulles, nous devons travailler dessus avant de les alimenter à notre modèle :

data.isnull().sum()
gender                    24
age                        0
no_of_days_subscribed      0
multi_screen               0
mail_subscribed            0
weekly_mins_watched        0
minimum_daily_mins         0
maximum_daily_mins         0
weekly_max_night_mins      0
videos_watched             0
maximum_days_inactive     28
customer_support_calls     0
churn                     35
dtype: int64
final_data = data.dropna()         # Dropping the null values

Échantillonnage des données

Échantillonnons les données car nos données sont très déséquilibrées. Le suréchantillonnage et le sous-échantillonnage ont été utilisés pour les classes minoritaires et majoritaires de la sortie. Enfin, les données sont concaténées pour un traitement ultérieur :

final_data["churn"].value_counts()       
# let us see how many data is there in each class for deciding the sampling data number
0.0    1665
1.0     253
Name: churn, dtype: int64

Fractionnement des données

Tout d'abord, nous devons diviser le cadre de données en classes distinctes avant l'échantillonnage :

data_majority = final_data[final_data['churn']==0] #class 0
data_minority = final_data[final_data['churn']==1] #class 1
#upsampling minority class
data_minority_upsampled = resample(data_minority, replace=True, n_samples=900, random_state=123) 
 #downsampling majority class
data_majority_downsampled = resample(data_majority, replace=False, n_samples=900, random_state=123)
#concanating both upsampled and downsampled class
## Data Concatenation:  Concatenating the dataframe after upsampling and downsampling 
#concanating both upsampled and downsampled class
data2 = pd.concat([data_majority_downsampled, data_minority_upsampled])
## Encoding Catagoricals:  We need to encode the categorical variables before feeding it to the model
data2[['gender', 'multi_screen', 'mail_subscribed']]
#label encoding categorical variables
label_encoder = preprocessing.LabelEncoder()
data2['gender']= label_encoder.fit_transform(data2['gender'])
data2['multi_screen']= label_encoder.fit_transform(data2['multi_screen'])
data2['mail_subscribed']= label_encoder.fit_transform(data2['mail_subscribed'])
## Lets now check again the distribution of the oputut class after sampling
data2["churn"].value_counts()
0.0    900
1.0    900
Name: churn, dtype: int64

Dans le code suivant, nous traitons de la mise à l'échelle, de la division des ensembles d'apprentissage et de test, et de la séparation des variables dépendantes et indépendantes :

#indenpendent variable 
X = data2.iloc[:,:-1]
## This X will be fed to the model to learn params 
#scaling the data
sc = StandardScaler()         # Bringing the mean to 0 and variance to 1, so as to have a non-noisy optimization
X = sc.fit_transform(X)
X = sc.transform(X)
## Keeping the output column in a separate dataframe
data2 = data2.sample(frac=1).reset_index(drop=True) ## Shuffle the data frame and reset index
n_samples, n_features = X.shape ## n_samples is the number of samples and n_features is the number of features
#output column
Y = data2["churn"]
#output column
Y = data2["churn"]
##Data Splitting: 
## The data is processed, so now we can split the data into train and test to train the model with training data and test it later from testing data.
#splitting data into train and test
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.30, random_state=42, stratify = Y)
print((y_train == 1).sum())
print((y_train == 0).sum())
630
630

Imprimons le type de train et de jeu de test :

print(type(X_train))
print(type(X_test))
print(type(y_train.values))
print(type(y_test.values))
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>

En les convertissant en tenseurs pendant que PyTorch fonctionne, nous utiliserons la torch.from_numpy()méthode :

X_train = torch.from_numpy(X_train.astype(np.float32))
X_test = torch.from_numpy(X_test.astype(np.float32))
y_train = torch.from_numpy(y_train.values.astype(np.float32))
y_test = torch.from_numpy(y_test.values.astype(np.float32))

En faisant du vecteur de sortie Y un vecteur colonne pour les multiplications matricielles, nous effectuons ce changement en utilisant l'opération de visualisation comme indiqué dans le code ci-dessous :

y_train = y_train.view(y_train.shape[0], 1)
y_test = y_test.view(y_test.shape[0], 1)

Fonction d'activation

Nous utilisons des fonctions d'activation pour représenter l'interaction dynamique dans les données linéaires. Ici, nous utilisons une fonction d'activation sigmoïde. Nous avons choisi la fonction sigmoïde car elle limitera la valeur de 0 à 1. Les fonctions d'activation aident à introduire de la non-linéarité dans la sortie d'un neurone, ce qui améliore la précision, l'efficacité de calcul et la vitesse de convergence.

Les fonctions d'activation doivent être différentiables et converger rapidement par rapport aux poids.

Inconvénients sigmoïdes :

  • Vanishing Gradient pendant la phase de rétropropagation d'un réseau de neurones (en particulier les RNN).
  • En raison de sa nature exponentielle, il est coûteux en temps de calcul.

Pour citer cet article : "La principale fonction d'activation qui a été largement utilisée est la fonction sigmoïde. Cependant, lorsque le Rectifier Linear Unit (ReLU) (Nair & Hinton, 2010) a été introduit, il est rapidement devenu un meilleur remplacement pour la fonction Sigmoid en raison de son impact positif sur les différentes tâches d'apprentissage automatique. Bien que l'utilisation de Sigmoid et le travail sur des couches moins profondes ne posent aucun problème, certains problèmes surviennent lorsque l'architecture devient plus profonde car les termes dérivés inférieurs à 1 seront multipliés les uns par les autres plusieurs fois pour que les valeurs deviennent plus petites. et plus petit jusqu'à ce que le gradient tende vers zéro donc disparaisse. En revanche, si les valeurs sont supérieures à un, l'inverse se produit, les nombres multipliés devenant de plus en plus gros jusqu'à tendre vers l'infini et faire exploser le gradient. Une bonne solution serait de garder les valeurs à 1 pour que même lorsqu'elles sont multipliées, elles ne changent pas. C'est exactement ce que fait ReLU : il a un gradient de 1 pour les entrées positives et de 0 pour les entrées négatives. »

Création de modèles : création d'un modèle de régression logistique dans Pytorch

Vous trouverez ci-dessous le code responsable de la création du modèle de régression logistique dans PyTorch :

#logistic regression class
class LogisticRegression(nn.Module):
    def __init__(self, n_input_features):
        super(LogisticRegression, self).__init__()
        self.linear = nn.Linear(n_input_features, 1)
    
    #sigmoid transformation of the input 
    def forward(self, x):
        y_pred = torch.sigmoid(self.linear(x))
        return y_pred

Vous devez déclarer les calques de votre modèle dans la __init__()méthode. Nous avons utilisé des couches linéaires, qui sont spécifiées à l'aide du torch.nnmodule. La couche peut recevoir n'importe quel nom, comme self.linear(dans notre cas). J'ai déclaré une couche linéaire parce que c'est une régression logistique.

La syntaxe est : torch.nn.Linear(in_features, out_features, bias=True)

Le forward()procédé est chargé d'effectuer la passe/propagation vers l'avant. L'entrée est acheminée via la couche précédemment établie et la sortie de cette couche est envoyée via la fonction d'activation sigmoïde.

Initialisons le modèle :

lr = LogisticRegression(n_features)

Compilation de modèles

Définissons le nombre d'époques et le taux d'apprentissage que nous voulons pour notre modèle d'entraînement. Comme les données sont binaires, nous utiliserons Binary Cross Entropy comme fonction de perte utilisée pour optimiser le modèle à l'aide d'un optimiseur SGD.

Perte BCE

Nous utiliserons également la fonction de perte (erreur) L pour évaluer les performances de notre algorithme. N'oubliez pas que la fonction de perte n'est appliquée qu'à un échantillon d'apprentissage et que la fonction de perte la plus généralement utilisée est une erreur quadratique. Cependant, la fonction de perte d'erreur au carré dans la régression logistique n'est pas la meilleure option. Cela produit un problème d'optimisation qui n'est pas convexe et l'approche de descente de gradient peut ne pas converger de manière optimale. Nous appliquerons la perte de BCE pour, espérons-le, atteindre l'optimum global.

La perte BCE signifie la perte d'entropie croisée binaire et est souvent utilisée dans les instances de classification binaire.

Il convient de noter que lors de l'utilisation de la fonction de perte BCE , la sortie du nœud doit être comprise entre (0 et 1). Pour cela, nous devrons utiliser un optimiseur approprié. Nous avons choisi SGD, ou Stochastic Gradient Descent, un optimiseur régulièrement utilisé. Les autres optimiseurs incluent Adam, Lars et d'autres.

Vous devez fournir les paramètres du modèle et le taux d'apprentissage en entrée de l'optimiseur. SGD choisit un point de données au hasard dans l'ensemble de données à chaque itération pour minimiser considérablement les calculs.

Il est également habituel d'échantillonner un petit nombre de points de données plutôt qu'un seul à chaque étape (souvent appelé descente de gradient en mini-lot). Le mini-lot tente d'équilibrer l'efficacité de la descente de gradient et la vitesse de SGD.

Taux d'apprentissage

Le taux d'apprentissage est un hyperparamètre ajustable utilisé dans l'entraînement des réseaux de neurones avec une valeur un peu positive, souvent de 0,0 à 0,1. Des taux d'apprentissage plus faibles nécessitent plus d'époques de formation en raison des changements plus mineurs dans les poids à chaque mise à jour, tandis que des taux d'apprentissage plus excellents produisent des changements rapides et nécessitent moins d'époques de formation.

Un taux d'apprentissage élevé peut conduire le modèle à converger trop rapidement vers une mauvaise solution, tandis qu'un faible taux d'apprentissage peut entraîner le blocage du processus. À l'aide des calendriers de taux d'apprentissage, vous pouvez modifier le taux d'apprentissage au fur et à mesure que la formation progresse.

Il ajuste le taux d'apprentissage en fonction d'un calendrier prédéfini, tel que basé sur le temps, basé sur les étapes ou exponentiel.

Nous pouvons créer un calendrier de taux d'apprentissage pour mettre à jour le taux d'apprentissage tout au long de la formation en fonction d'une règle prédéfinie. Le planificateur de taux d'apprentissage le plus courant est une décroissance par étapes, qui réduit le taux d'apprentissage d'un certain pourcentage après un certain nombre d'époques d'entraînement. Enfin, nous pouvons dire qu'un calendrier de taux d'apprentissage est une structure prédéterminée pour ajuster le taux d'apprentissage à travers les époques ou les itérations au fur et à mesure que la formation se produit.

Voici deux des stratégies les plus répandues pour apprendre les horaires de taux :

  • Décroissance du taux d'apprentissage : elle se produit lorsque nous choisissons un taux d'apprentissage initial, puis que nous le réduisons progressivement conformément à un ordonnanceur.
  • Taux d'apprentissage constant : Comme son nom l'indique, nous fixons un taux d'apprentissage et ne le faisons pas varier tout au long de la formation.

Remarque : Le taux d'apprentissage est un hyperparamètre qui doit être modifié. Au lieu d'utiliser un taux d'apprentissage constant, nous pourrions commencer avec une valeur plus élevée de LR, puis la diminuer progressivement après un nombre spécifique de tours. Cela nous permet d'avoir une convergence plus rapide au début tout en réduisant les risques de dépassement de la perte.

Dans PyTorch, nous pouvons utiliser plusieurs planificateurs du optimpackage. Vous pouvez suivre ce lien pour voir comment vous pouvez ajuster le taux d'apprentissage d'un réseau de neurones à l'aide de PyTorch. Définissons les paramètres, la perte et l'optimiseur :

num_epochs = 500                                        
# Traning the model for large number of epochs to see better results  
learning_rate = 0.0001                               
criterion = nn.BCELoss()                                
# We are working on lgistic regression so using Binary Cross Entropy
optimizer = torch.optim.SGD(lr.parameters(), lr=learning_rate)      
# Using ADAM optimizer to find local minima   

Suivi du processus de formation :

for epoch in range(num_epochs):
    y_pred = lr(X_train)
    loss = criterion(y_pred, y_train)             
    loss.backward()
    optimizer.step()
    optimizer.zero_grad()
    if (epoch+1) % 20 == 0:                                         
        # printing loss values on every 10 epochs to keep track
        print(f'epoch: {epoch+1}, loss = {loss.item():.4f}')
epoch: 20, loss = 0.8447
epoch: 40, loss = 0.8379
epoch: 60, loss = 0.8316
epoch: 80, loss = 0.8257
epoch: 100, loss = 0.8203
epoch: 120, loss = 0.8152
epoch: 140, loss = 0.8106
epoch: 160, loss = 0.8063
epoch: 180, loss = 0.8023
epoch: 200, loss = 0.7986
epoch: 220, loss = 0.7952
epoch: 240, loss = 0.7920
epoch: 260, loss = 0.7891
epoch: 280, loss = 0.7863
epoch: 300, loss = 0.7838
epoch: 320, loss = 0.7815
epoch: 340, loss = 0.7793
epoch: 360, loss = 0.7773
epoch: 380, loss = 0.7755
epoch: 400, loss = 0.7737
epoch: 420, loss = 0.7721
epoch: 440, loss = 0.7706
epoch: 460, loss = 0.7692
epoch: 480, loss = 0.7679
epoch: 500, loss = 0.7667

Ici, la première passe avant se produit. Ensuite, la perte est calculée. Lorsqu'il loss.backward()est appelé, il calcule le gradient de perte par rapport aux poids (de la couche). Les poids sont ensuite mis à jour en appelant optimizer.step(). Après cela, les poids doivent être vidés pour la prochaine itération. La zero_grad()méthode est donc appelée.

Le code ci-dessus imprime la perte à chaque 20e époque.

Performances du modèle

Voyons enfin la précision du modèle :

with torch.no_grad():
    y_predicted = lr(X_test)
    y_predicted_cls = y_predicted.round()
    acc = y_predicted_cls.eq(y_test).sum() / float(y_test.shape[0])
    print(f'accuracy: {acc.item():.4f}')
accuracy: 0.5093

Nous devons utiliser torch.no_grad()ici. Le but est d'omettre le calcul du gradient sur les poids. Ainsi, tout ce que je mettrai dans cette boucle ne modifiera pas les poids et ne perturbera donc pas le processus de rétropropagation.

Nous pouvons également voir la précision, le rappel et le score F1 à l'aide du rapport de classification :

#classification report
from sklearn.metrics import classification_report
print(classification_report(y_test, y_predicted_cls))
              precision    recall  f1-score   support

         0.0       0.51      0.62      0.56       270
         1.0       0.51      0.40      0.45       270

    accuracy                           0.51       540
   macro avg       0.51      0.51      0.50       540
weighted avg       0.51      0.51      0.50       540

Visualisation de la matrice de confusion :

#confusion matrix
from sklearn.metrics import confusion_matrix
confusion_matrix = confusion_matrix(y_test, y_predicted_cls)
print(confusion_matrix)
[[168 102]
 [163 107]]

Conclusion

Notez que notre modèle ne fonctionne pas bien sur cet ensemble de données relativement complexe. Le but de ce tutoriel est de vous montrer comment faire une régression logistique dans PyTorch. Si vous souhaitez obtenir une meilleure précision et d'autres mesures, envisagez d'affiner les hyperparamètres de formation, comme l'augmentation du nombre d'époques et du taux d'apprentissage ou même l'ajout d'une couche supplémentaire (c'est-à-dire le réseau de neurones).

Obtenez le code complet dans ce notebook Colab .

Source de l'article d'origine sur https://www.thepythoncode.com

中條 美冬

1650549914

PyTorchを使用してPythonでロジスティック回帰アルゴリズムを実行する方法

PythonでPyTorchを使用したロジスティック回帰

PythonのカスタマーチャーンサンプルデータセットでPyTorchディープラーニングフレームワークを使用してロジスティック回帰アルゴリズムを実行する方法を学びます。

ロジスティック回帰は、入力変数が与えられた場合の離散結果の確率を記述するために使用される確率モデルです。名前にもかかわらず、ロジスティック回帰は分類モデルであり、回帰モデルではありません。一言で言えば、ロジスティック回帰は、分類を除いて線形回帰に似ています。

シグモイド関数を使用して結果の確率を計算します。入力変数の非線形変換のため、ロジスティック回帰では、入力変数と出力変数の間の線形相関は必要ありません。

このチュートリアルでは、PyTorchで顧客の離職を予測するためのロジスティック回帰モデルの開発に焦点を当てています。

ロジスティック回帰とは何ですか

メソッドの名前には「回帰」という用語が含まれていますが、これは本質的に、分類の問題を処理するために設計された教師あり機械学習手法です。これは、0から1の範囲のロジスティック関数のアルゴリズムの使用によるものです。

その結果、ロジスティック回帰を使用して、単一の特徴変数(X)が特定のカテゴリ(Y)に属する可能性を予測できます。

ロジスティック回帰は、分類の問題ではなく数値を予測するために線形回帰が使用されますが、線形回帰と多くの類似点があります。どちらの戦略も、線を使用してターゲット変数を示します。

線形回帰は線をデータに適合させて新しい量を予測し、ロジスティック回帰は線を適合させて2つのクラスを最適に分離します。

シグモイド関数

ロジスティック回帰とは何か、およびそれがどのように機能するかを理解するには、最初にシグモイド関数と自然対数関数を把握する必要があります。

この図は、0から1の範囲のx値に対する変数のS字型の曲線を示しています。

シグモイド関数

出典:DepositPhotos

その定義域のほとんどで、シグモイド関数の値は0または1に非常に近くなっています。このため、二項分類問題での使用に適しています。下の図を見て、ロジスティック回帰モデルがどのように表されているかを確認してください。

ロジスティック回帰モデル

 

出典:datahacker

ご覧のとおり、線形関数zの出力を計算することから始めます。シグモイド関数は、この出力zを入力として受け取ります。続いて、計算されたzについて、zが決定する予測ŷを生成します。zが有意な正の数である場合、ŷは1に近くなります。

一方、zの数が大幅に負の場合、ŷはゼロに近くなります。

したがって、ŷは常に0から1の範囲になります。しきい値0.5を使用することは、予測ŷを分類するための簡単な手法です。

予測が0.5よりも広範囲である場合、ŷは1に等しいと想定します。

そうでなければ、ŷはゼロに等しいと仮定します。ロジスティック回帰を適用する場合、目的は、パラメーターwおよびbを計算して、ŷがŷ=1の尤度の適切な推定値になるようにすることです。

データの説明

この調査で使用されたデータセットは、さまざまな変数に基づいた顧客の離職に関する情報を提供します。データセットには、チャーンの予測に使用できる2000行と15の機能があります。こちらからダウンロードでき ます。

関連: 顧客チャーン予測:Pythonの完全ガイド

このチュートリアルの依存関係をインストールしましょう:

$ pip install matplotlib numpy pandas scikit_learn==1.0.2 torch==1.10.1

データセットを自動的にダウンロードするには、次を使用できますgdown

$ pip install --upgrade gdown
$ gdown --id 12vfq3DYFId3bsXuNj_PhsACMzrLTfObs

始めましょう:

import pandas as pd
import numpy as np
import torch
import torch.nn as nn
from sklearn.utils import resample
from sklearn import preprocessing
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from warnings import filterwarnings
filterwarnings('ignore')

データを読み取り、、、 列を削除して、データの形状を見てみましょyearう 。customer_idphone_no

#reading data
data = pd.read_csv("data_regression.csv")
##The dimension of the data is seen, and the output column is checked to see whether it is continuous or discrete. 
##In this case, the output is discrete, so a classification algorithm should be applied.
data = data.drop(["year", "customer_id", "phone_no"], axis=1)
print(data.shape)         # Lookiing the shape of the data
print(data.columns)       # Looking how many columns data has
data.dtypes  
data.head()

ヌル値の処理

null値がある場合は、モデルにフィードする前にそれに取り組む必要があります。

data.isnull().sum()
gender                    24
age                        0
no_of_days_subscribed      0
multi_screen               0
mail_subscribed            0
weekly_mins_watched        0
minimum_daily_mins         0
maximum_daily_mins         0
weekly_max_night_mins      0
videos_watched             0
maximum_days_inactive     28
customer_support_calls     0
churn                     35
dtype: int64
final_data = data.dropna()         # Dropping the null values

データサンプリング

データのバランスが非常に悪いため、データをサンプリングしてみましょう。アップサンプリングとダウンサンプリングは、出力の少数派と多数派のクラスに使用されています。最後に、データはさらに処理するために連結されます。

final_data["churn"].value_counts()       
# let us see how many data is there in each class for deciding the sampling data number
0.0    1665
1.0     253
Name: churn, dtype: int64

データ分割

まず、サンプリングする前に、データフレームを個別のクラスに分割する必要があります。

data_majority = final_data[final_data['churn']==0] #class 0
data_minority = final_data[final_data['churn']==1] #class 1
#upsampling minority class
data_minority_upsampled = resample(data_minority, replace=True, n_samples=900, random_state=123) 
 #downsampling majority class
data_majority_downsampled = resample(data_majority, replace=False, n_samples=900, random_state=123)
#concanating both upsampled and downsampled class
## Data Concatenation:  Concatenating the dataframe after upsampling and downsampling 
#concanating both upsampled and downsampled class
data2 = pd.concat([data_majority_downsampled, data_minority_upsampled])
## Encoding Catagoricals:  We need to encode the categorical variables before feeding it to the model
data2[['gender', 'multi_screen', 'mail_subscribed']]
#label encoding categorical variables
label_encoder = preprocessing.LabelEncoder()
data2['gender']= label_encoder.fit_transform(data2['gender'])
data2['multi_screen']= label_encoder.fit_transform(data2['multi_screen'])
data2['mail_subscribed']= label_encoder.fit_transform(data2['mail_subscribed'])
## Lets now check again the distribution of the oputut class after sampling
data2["churn"].value_counts()
0.0    900
1.0    900
Name: churn, dtype: int64

次のコードでは、スケーリング、トレーニングセットとテストセットの分割、および従属変数と独立変数の分離を扱います。

#indenpendent variable 
X = data2.iloc[:,:-1]
## This X will be fed to the model to learn params 
#scaling the data
sc = StandardScaler()         # Bringing the mean to 0 and variance to 1, so as to have a non-noisy optimization
X = sc.fit_transform(X)
X = sc.transform(X)
## Keeping the output column in a separate dataframe
data2 = data2.sample(frac=1).reset_index(drop=True) ## Shuffle the data frame and reset index
n_samples, n_features = X.shape ## n_samples is the number of samples and n_features is the number of features
#output column
Y = data2["churn"]
#output column
Y = data2["churn"]
##Data Splitting: 
## The data is processed, so now we can split the data into train and test to train the model with training data and test it later from testing data.
#splitting data into train and test
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.30, random_state=42, stratify = Y)
print((y_train == 1).sum())
print((y_train == 0).sum())
630
630

列車の種類とテストセットを印刷してみましょう。

print(type(X_train))
print(type(X_test))
print(type(y_train.values))
print(type(y_test.values))
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>

PyTorchの動作中にそれらをテンソルに変換するには、次のtorch.from_numpy()方法を使用します。

X_train = torch.from_numpy(X_train.astype(np.float32))
X_test = torch.from_numpy(X_test.astype(np.float32))
y_train = torch.from_numpy(y_train.values.astype(np.float32))
y_test = torch.from_numpy(y_test.values.astype(np.float32))

出力ベクトルYを行列乗算の列ベクトルとして作成し、次のコードに示すように、ビュー操作を使用してこの変更を実行します。

y_train = y_train.view(y_train.shape[0], 1)
y_test = y_test.view(y_test.shape[0], 1)

活性化関数

線形データの動的相互作用を表すために、活性化関数を使用します。ここでは、シグモイド活性化関数を利用します。値を0から1に制限するため、シグモイド関数を選択しました。活性化関数は、ニューロンの出力に非線形性を導入するのに役立ち、精度、計算効率、および収束速度を向上させます。

活性化関数は、重みに関して微分可能で高速に収束する必要があります。

シグモイドの欠点:

  • ニューラルネットワーク(特にRNN)のバックプロパゲーション段階での勾配消失。
  • その指数関数的な性質のため、計算コストが高くなります。

この論文から引用するには: "広く使われていた主な活性化関数はシグモイド関数です。ただし、Rectifier Linear Unit(ReLU)(Nair&Hinton、2010)が導入されたとき、さまざまな機械学習タスクにプラスの影響を与えるため、すぐにSigmoid関数のより良い代替品になりました。シグモイドを使用して浅いレイヤーで作業しても問題はありませんが、アーキテクチャが深くなると、1未満の微分項が何度も乗算されて値が小さくなるため、いくつかの問題が発生します。勾配がゼロに近づくまで小さくなり、それによって消えます。一方、値が1より大きい場合は、逆のことが起こり、乗算される数値は、無限大になり勾配が爆発するまで、ますます大きくなります。良い解決策は、値を1に保ち、乗算しても変化しないようにすることです。これはまさにReLUが行うことです。正の入力の場合は勾配1、負の入力の場合は勾配0があります。」

モデル構築:Pytorchでのロジスティック回帰モデルの作成

以下は、PyTorchでロジスティック回帰モデルを構築するための責任のあるコードです。

#logistic regression class
class LogisticRegression(nn.Module):
    def __init__(self, n_input_features):
        super(LogisticRegression, self).__init__()
        self.linear = nn.Linear(n_input_features, 1)
    
    #sigmoid transformation of the input 
    def forward(self, x):
        y_pred = torch.sigmoid(self.linear(x))
        return y_pred

__init__()メソッドでモデルのレイヤーを宣言する必要があります。torch.nnモジュールを使用して指定された線形レイヤーを使用しました。self.linearレイヤーには、(この場合)などの任意の名前を付けることができます。ロジスティック回帰であるため、1つの線形レイヤーを宣言しました。

構文は次のとおりです。 torch.nn.Linear(in_features, out_features, bias=True)

このforward()方法は、フォワードパス/伝播の実行を担当します。入力は以前に確立されたレイヤーを介してルーティングされ、そのレイヤーの出力はシグモイド活性化関数を介して送信されます。

モデルを初期化しましょう:

lr = LogisticRegression(n_features)

モデルのコンパイル

エポックの数と、トレーニング用のモデルに必要な学習率を定義しましょう。データはバイナリであるため、SGDオプティマイザを使用してモデルを最適化するために使用される損失関数としてバイナリクロスエントロピーを使用します。

BCE損失

また、損失(誤差)関数Lを使用して、アルゴリズムのパフォーマンスを評価します。損失関数は1つのトレーニングサンプルにのみ適用され、最も一般的に使用される損失関数は二乗誤差であることを忘れないでください。ただし、ロジスティック回帰の二乗誤差損失関数は最良のオプションではありません。凸ではない最適化問題が発生し、最急降下法は最適に収束しない可能性があります。BCE損失を適用して、グローバル最適に到達できることを願っています。

BCE損失は、バイナリクロスエントロピー損失の略で、バイナリ分類インスタンスでよく使用されます。

BCE損失関数を使用する場合、ノードの出力は(0–1)の間にある必要があることに注意してください。このためには、適切なオプティマイザを使用する必要があります。SGD、または定期的に使用されるオプティマイザーである確率的勾配降下法を選択しました。他のオプティマイザーには、Adam、Larsなどがあります。

オプティマイザーへの入力として、モデルパラメーターと学習率を提供する必要があります。SGDは、計算を大幅に最小化するために、各反復でデータセット全体からランダムに1つのデータポイントを選択します。

また、各ステップで1つだけではなく、少数のデータポイントをサンプリングすることも通常です(ミニバッチ最急降下法と呼ばれることがよくあります)。ミニバッチは、勾配降下法の効率とSGDの速度のバランスをとろうとします。

学習率

学習率は、ニューラルネットワークトレーニングで使用される調整可能なハイパーパラメータであり、多くの場合0.0〜0.1の正の値が少しあります。学習率が低いと、更新ごとに重みがわずかに変化するため、より多くのトレーニングエポックが必要になります。一方、学習率が優れていると、変更が迅速になり、必要なトレーニングエポックが少なくなります。

学習率が高いと、モデルが不十分なソリューションに急速に収束する可能性がありますが、学習率が低いと、プロセスが停止する可能性があります。学習率スケジュールを使用すると、トレーニングの進行に合わせて学習率を変更できます。

時間ベース、ステップベース、指数関数など、事前定義されたスケジュールに基づいて学習率を調整します。

事前定義されたルールに基づいて、トレーニング全体で学習率を更新するための学習率スケジュールを作成する場合があります。最も一般的な学習率スケジューラーはステップ減衰です。これは、特定の数のトレーニングエポックの後に学習率を特定の割合で減らします。最後に、学習率スケジュールは、トレーニングが発生するときにエポックまたは反復にわたって学習率を調整するための所定の構造であると言えます。

以下は、レートスケジュールを学習するための最も一般的な戦略の2つです。

  • 学習率の低下:これは、初期学習率を選択し、スケジューラーに従って徐々にそれを下げるときに発生します。
  • 一定の学習率:名前が示すように、学習率を設定し、トレーニング全体で変化させません。

注:学習率は、微調整する必要のあるハイパーパラメーターです。一定の学習率を使用する代わりに、LRの値を高くして開始し、特定のラウンド数の後に徐々に値を下げることができます。これにより、損失をオーバーシュートするリスクを低減しながら、最初はより迅速に収束することができます。

PyTorchでは、optimパッケージから複数のスケジューラーを利用する場合があります。このリンクをたどると、PyTorchを使用してニューラルネットワークの学習率を調整する方法を確認できます。パラメータ、損失、オプティマイザを定義しましょう。

num_epochs = 500                                        
# Traning the model for large number of epochs to see better results  
learning_rate = 0.0001                               
criterion = nn.BCELoss()                                
# We are working on lgistic regression so using Binary Cross Entropy
optimizer = torch.optim.SGD(lr.parameters(), lr=learning_rate)      
# Using ADAM optimizer to find local minima   

トレーニングプロセスの追跡:

for epoch in range(num_epochs):
    y_pred = lr(X_train)
    loss = criterion(y_pred, y_train)             
    loss.backward()
    optimizer.step()
    optimizer.zero_grad()
    if (epoch+1) % 20 == 0:                                         
        # printing loss values on every 10 epochs to keep track
        print(f'epoch: {epoch+1}, loss = {loss.item():.4f}')
epoch: 20, loss = 0.8447
epoch: 40, loss = 0.8379
epoch: 60, loss = 0.8316
epoch: 80, loss = 0.8257
epoch: 100, loss = 0.8203
epoch: 120, loss = 0.8152
epoch: 140, loss = 0.8106
epoch: 160, loss = 0.8063
epoch: 180, loss = 0.8023
epoch: 200, loss = 0.7986
epoch: 220, loss = 0.7952
epoch: 240, loss = 0.7920
epoch: 260, loss = 0.7891
epoch: 280, loss = 0.7863
epoch: 300, loss = 0.7838
epoch: 320, loss = 0.7815
epoch: 340, loss = 0.7793
epoch: 360, loss = 0.7773
epoch: 380, loss = 0.7755
epoch: 400, loss = 0.7737
epoch: 420, loss = 0.7721
epoch: 440, loss = 0.7706
epoch: 460, loss = 0.7692
epoch: 480, loss = 0.7679
epoch: 500, loss = 0.7667

ここで、最初のフォワードパスが発生します。次に、損失が計算されます。がloss.backward()呼び出されると、(レイヤーの)重みに関する損失勾配が計算されます。次に、を呼び出すことによって重みが更新されoptimizer.step()ます。この後、次の反復のために重みを空にする必要があります。したがって、zero_grad()メソッドが呼び出されます。

上記のコードは、20番目のエポックごとに損失を出力します。

モデルのパフォーマンス

最後に、モデルの精度を見てみましょう。

with torch.no_grad():
    y_predicted = lr(X_test)
    y_predicted_cls = y_predicted.round()
    acc = y_predicted_cls.eq(y_test).sum() / float(y_test.shape[0])
    print(f'accuracy: {acc.item():.4f}')
accuracy: 0.5093

ここで使用する必要がtorch.no_grad()あります。目標は、重みに対する勾配計算を省略することです。したがって、このループ内に配置したものは重みを変更せず、したがってバックプロパゲーションプロセスを中断しません。

分類レポートを使用して、適合率、再現率、およびF1スコアを確認することもできます。

#classification report
from sklearn.metrics import classification_report
print(classification_report(y_test, y_predicted_cls))
              precision    recall  f1-score   support

         0.0       0.51      0.62      0.56       270
         1.0       0.51      0.40      0.45       270

    accuracy                           0.51       540
   macro avg       0.51      0.51      0.50       540
weighted avg       0.51      0.51      0.50       540

混同行列の視覚化:

#confusion matrix
from sklearn.metrics import confusion_matrix
confusion_matrix = confusion_matrix(y_test, y_predicted_cls)
print(confusion_matrix)
[[168 102]
 [163 107]]

結論

このモデルは、この比較的複雑なデータセットではうまく機能しないことに注意してください。このチュートリアルの目的は、PyTorchでロジスティック回帰を実行する方法を示すことです。精度やその他の指標を向上させたい場合は、エポック数と学習率を増やしたり、さらに1つのレイヤー(ニューラルネットワーク)を追加したりするなど、トレーニングハイパーパラメーターを微調整することを検討してください。

このColabノートブックで完全なコードを入手してください。

https://www.thepythoncode.comの元の記事のソース

Hoang  Kim

Hoang Kim

1650545712

Hồi quy logistic sử dụng PyTorch trong Python

Cách thực hiện thuật toán hồi quy logistic trong Python với PyTorch

Tìm hiểu cách thực hiện thuật toán hồi quy logistic bằng cách sử dụng khung học tập sâu PyTorch trên tập dữ liệu ví dụ về khách hàng bằng Python.

Hồi quy logistic là một mô hình xác suất được sử dụng để mô tả xác suất của các kết quả rời rạc cho các biến đầu vào. Mặc dù tên gọi, hồi quy logistic là một mô hình phân loại, không phải là một mô hình hồi quy. Tóm lại, hồi quy logistic tương tự như hồi quy tuyến tính ngoại trừ việc phân loại.

Nó tính toán xác suất của kết quả bằng cách sử dụng hàm sigmoid. Do sự biến đổi phi tuyến tính của biến đầu vào, hồi quy logistic không cần tương quan tuyến tính giữa các biến đầu vào và đầu ra.

Hướng dẫn này tập trung vào việc phát triển mô hình hồi quy logistic để dự báo mức tiêu hao của khách hàng trong PyTorch.

Hồi quy logistic là gì

Mặc dù tên của phương pháp bao gồm thuật ngữ "hồi quy", về cơ bản nó là một kỹ thuật học máy có giám sát được thiết kế để xử lý các vấn đề phân loại. Đó là do thuật toán sử dụng hàm logistic, nằm trong khoảng từ 0 đến 1.

Do đó, chúng ta có thể sử dụng hồi quy logistic để dự báo khả năng một biến đặc trưng (X) thuộc về một danh mục cụ thể (Y).

Hồi quy logistic có nhiều điểm tương đồng với hồi quy tuyến tính, mặc dù hồi quy tuyến tính được sử dụng để dự đoán các giá trị số hơn là các vấn đề phân loại. Cả hai chiến lược đều sử dụng một đường thẳng để mô tả biến mục tiêu.

Hồi quy tuyến tính phù hợp với một dòng với dữ liệu để dự đoán một số lượng mới, trong khi hồi quy logistic phù hợp với một dòng để tách hai lớp một cách tối ưu.

Chức năng Sigmoid

Để hiểu hồi quy logistic là gì và nó hoạt động như thế nào, trước tiên bạn phải nắm được hàm sigmoid và hàm logarit tự nhiên.

Hình này mô tả đường cong hình chữ S của một biến cho các giá trị x nằm trong khoảng từ 0 đến 1:

Chức năng Sigmoid

Nguồn: DepositPhotos

Trong hầu hết miền của nó, hàm sigmoid có các giá trị cực kỳ gần bằng 0 hoặc 1. Do đó, nó thích hợp để sử dụng trong các bài toán phân loại nhị phân. Hãy xem hình bên dưới để xem mô hình hồi quy logistic được biểu diễn như thế nào:

Mô hình hồi quy logistic

 

Nguồn: datahacker

Như bạn có thể thấy, chúng ta sẽ bắt đầu bằng cách tính toán đầu ra của một hàm tuyến tính, z. Hàm sigmoid sẽ lấy đầu ra z này làm đầu vào. Sau đó, đối với z được tính toán, chúng tôi sẽ tạo ra dự đoán ŷ, mà z sẽ xác định. Nếu z là một số dương có nghĩa thì ŷ sẽ gần bằng một.

Mặt khác, nếu z có một số âm đáng kể, ŷ sẽ gần bằng không.

Do đó, ŷ sẽ luôn nằm trong khoảng từ 0 đến 1. Sử dụng giá trị ngưỡng 0,5 là một kỹ thuật đơn giản để phân loại dự đoán ŷ.

Nếu dự báo của chúng tôi rộng hơn 0,5, chúng tôi giả định ŷ bằng 1.

Nếu không, chúng ta sẽ giả sử rằng ŷ bằng không. Khi chúng ta áp dụng hồi quy logistic, mục tiêu của chúng ta là cố gắng tính toán các tham số w và b để ŷ trở thành một ước lượng phù hợp về khả năng ŷ = 1.

mô tả dữ liệu

Bộ dữ liệu được sử dụng trong nghiên cứu này cung cấp thông tin về mức độ tiêu hao của khách hàng dựa trên các biến số khác nhau. Tập dữ liệu có 2000 hàng và 15 tính năng có thể được sử dụng để dự đoán thời gian gián đoạn. Nó có thể được tải xuống  ở đây .

Liên quan: Dự đoán Churn của Khách hàng: Hướng dẫn Hoàn chỉnh bằng Python .

Hãy cài đặt các phụ thuộc của hướng dẫn này:

$ pip install matplotlib numpy pandas scikit_learn==1.0.2 torch==1.10.1

Để tự động tải xuống tập dữ liệu, chúng ta có thể sử dụng gdown:

$ pip install --upgrade gdown
$ gdown --id 12vfq3DYFId3bsXuNj_PhsACMzrLTfObs

Bắt đầu nào:

import pandas as pd
import numpy as np
import torch
import torch.nn as nn
from sklearn.utils import resample
from sklearn import preprocessing
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from warnings import filterwarnings
filterwarnings('ignore')

Hãy đọc dữ liệu ,  thả year,,  cột và xem hình dạng của dữ liệu:customer_idphone_no

#reading data
data = pd.read_csv("data_regression.csv")
##The dimension of the data is seen, and the output column is checked to see whether it is continuous or discrete. 
##In this case, the output is discrete, so a classification algorithm should be applied.
data = data.drop(["year", "customer_id", "phone_no"], axis=1)
print(data.shape)         # Lookiing the shape of the data
print(data.columns)       # Looking how many columns data has
data.dtypes  
data.head()

Điều trị Giá trị Vô hiệu

Nếu chúng ta có các giá trị null, chúng ta cần làm việc trên đó trước khi đưa nó vào mô hình của mình:

data.isnull().sum()
gender                    24
age                        0
no_of_days_subscribed      0
multi_screen               0
mail_subscribed            0
weekly_mins_watched        0
minimum_daily_mins         0
maximum_daily_mins         0
weekly_max_night_mins      0
videos_watched             0
maximum_days_inactive     28
customer_support_calls     0
churn                     35
dtype: int64
final_data = data.dropna()         # Dropping the null values

Lấy mẫu dữ liệu

Hãy để chúng tôi lấy mẫu dữ liệu vì dữ liệu của chúng tôi rất mất cân bằng. Lấy mẫu ngược và lấy mẫu xuống đã được sử dụng cho các tầng lớp thiểu số và đa số của đầu ra. Cuối cùng, dữ liệu được nối để xử lý thêm:

final_data["churn"].value_counts()       
# let us see how many data is there in each class for deciding the sampling data number
0.0    1665
1.0     253
Name: churn, dtype: int64

Tách dữ liệu

Đầu tiên, chúng ta cần chia khung dữ liệu thành các lớp riêng biệt trước khi lấy mẫu:

data_majority = final_data[final_data['churn']==0] #class 0
data_minority = final_data[final_data['churn']==1] #class 1
#upsampling minority class
data_minority_upsampled = resample(data_minority, replace=True, n_samples=900, random_state=123) 
 #downsampling majority class
data_majority_downsampled = resample(data_majority, replace=False, n_samples=900, random_state=123)
#concanating both upsampled and downsampled class
## Data Concatenation:  Concatenating the dataframe after upsampling and downsampling 
#concanating both upsampled and downsampled class
data2 = pd.concat([data_majority_downsampled, data_minority_upsampled])
## Encoding Catagoricals:  We need to encode the categorical variables before feeding it to the model
data2[['gender', 'multi_screen', 'mail_subscribed']]
#label encoding categorical variables
label_encoder = preprocessing.LabelEncoder()
data2['gender']= label_encoder.fit_transform(data2['gender'])
data2['multi_screen']= label_encoder.fit_transform(data2['multi_screen'])
data2['mail_subscribed']= label_encoder.fit_transform(data2['mail_subscribed'])
## Lets now check again the distribution of the oputut class after sampling
data2["churn"].value_counts()
0.0    900
1.0    900
Name: churn, dtype: int64

Trong đoạn mã sau, chúng tôi xử lý việc mở rộng quy mô, chia nhỏ các tập huấn luyện và thử nghiệm cũng như tách các biến phụ thuộc và độc lập:

#indenpendent variable 
X = data2.iloc[:,:-1]
## This X will be fed to the model to learn params 
#scaling the data
sc = StandardScaler()         # Bringing the mean to 0 and variance to 1, so as to have a non-noisy optimization
X = sc.fit_transform(X)
X = sc.transform(X)
## Keeping the output column in a separate dataframe
data2 = data2.sample(frac=1).reset_index(drop=True) ## Shuffle the data frame and reset index
n_samples, n_features = X.shape ## n_samples is the number of samples and n_features is the number of features
#output column
Y = data2["churn"]
#output column
Y = data2["churn"]
##Data Splitting: 
## The data is processed, so now we can split the data into train and test to train the model with training data and test it later from testing data.
#splitting data into train and test
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.30, random_state=42, stratify = Y)
print((y_train == 1).sum())
print((y_train == 0).sum())
630
630

Hãy in loại tàu và bộ thử nghiệm:

print(type(X_train))
print(type(X_test))
print(type(y_train.values))
print(type(y_test.values))
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>

Chuyển đổi chúng thành tensor khi PyTorch hoạt động, chúng tôi sẽ sử dụng torch.from_numpy()phương pháp:

X_train = torch.from_numpy(X_train.astype(np.float32))
X_test = torch.from_numpy(X_test.astype(np.float32))
y_train = torch.from_numpy(y_train.values.astype(np.float32))
y_test = torch.from_numpy(y_test.values.astype(np.float32))

Tạo vectơ đầu ra Y làm vectơ cột cho phép nhân ma trận, chúng tôi thực hiện thay đổi này bằng cách sử dụng thao tác xem như được hiển thị trong đoạn mã dưới đây:

y_train = y_train.view(y_train.shape[0], 1)
y_test = y_test.view(y_test.shape[0], 1)

Chức năng kích hoạt

Chúng tôi sử dụng các hàm kích hoạt để biểu diễn tương tác động trong dữ liệu tuyến tính. Ở đây, chúng tôi sử dụng chức năng kích hoạt sigmoid. Chúng tôi đã chọn hàm sigmoid vì nó sẽ giới hạn giá trị từ 0 đến 1. Các hàm kích hoạt hỗ trợ đưa tính không tuyến tính vào đầu ra của nơ-ron, giúp cải thiện độ chính xác, hiệu quả tính toán và tốc độ hội tụ.

Các chức năng kích hoạt phải khác biệt và hội tụ nhanh chóng đối với các trọng số.

Nhược điểm của Sigmoid:

  • Biến đổi Gradient trong giai đoạn lan truyền ngược của mạng nơ-ron (đặc biệt là RNN).
  • Vì tính chất hàm mũ nên nó rất tốn kém về mặt tính toán.

Trích dẫn từ bài báo này : "Chức năng kích hoạt chính được sử dụng rộng rãi là chức năng Sigmoid. Tuy nhiên, khi Bộ chỉnh lưu tuyến tính (ReLU) (Nair & Hinton, 2010) được giới thiệu, nó sớm trở thành một sự thay thế tốt hơn cho chức năng Sigmoid do tác động tích cực của nó đối với các tác vụ học máy khác nhau. Mặc dù việc sử dụng Sigmoid và làm việc trên các lớp nông hơn không gây ra bất kỳ vấn đề gì, nhưng một số vấn đề phát sinh khi kiến ​​trúc trở nên sâu hơn vì các số hạng phái sinh nhỏ hơn 1 sẽ được nhân với nhau nhiều lần và các giá trị sẽ trở nên nhỏ hơn. và nhỏ hơn cho đến khi gradient có xu hướng về 0 do đó biến mất. Mặt khác, nếu các giá trị lớn hơn một, điều ngược lại sẽ xảy ra, với các số được nhân ngày càng lớn hơn cho đến khi chúng có xu hướng vô cùng và làm bùng nổ gradient. Một giải pháp tốt sẽ là giữ các giá trị bằng 1 để ngay cả khi chúng được nhân lên, chúng vẫn không thay đổi. Đây chính xác là những gì ReLU làm: nó có gradient 1 cho các đầu vào tích cực và 0 cho các đầu vào tiêu cực .. "

Xây dựng mô hình: Tạo mô hình hồi quy logistic trong Pytorch

Dưới đây là mã chịu trách nhiệm để xây dựng mô hình Hồi quy logistic trong PyTorch:

#logistic regression class
class LogisticRegression(nn.Module):
    def __init__(self, n_input_features):
        super(LogisticRegression, self).__init__()
        self.linear = nn.Linear(n_input_features, 1)
    
    #sigmoid transformation of the input 
    def forward(self, x):
        y_pred = torch.sigmoid(self.linear(x))
        return y_pred

Bạn phải khai báo các lớp trong mô hình của mình trong __init__()phương thức. Chúng tôi đã sử dụng các lớp tuyến tính, được chỉ định bằng cách sử dụng torch.nnmô-đun. Lớp có thể được đặt bất kỳ tên nào, chẳng hạn như self.linear(trong trường hợp của chúng tôi). Tôi đã khai báo một lớp tuyến tính vì đó là hồi quy logistic.

Cú pháp là: torch.nn.Linear(in_features, out_features, bias=True)

Phương forward()thức phụ trách việc tiến hành chuyển tiếp / lan truyền. Đầu vào được định tuyến qua lớp đã thiết lập trước đó và đầu ra của lớp đó được gửi qua chức năng kích hoạt sigmoid.

Hãy khởi tạo mô hình:

lr = LogisticRegression(n_features)

Biên dịch mô hình

Hãy để chúng tôi xác định số kỷ nguyên và tỷ lệ học tập mà chúng tôi muốn mô hình đào tạo của chúng tôi. Vì dữ liệu là nhị phân, chúng tôi sẽ sử dụng Binary Cross Entropy làm hàm mất mát được sử dụng để tối ưu hóa mô hình bằng trình tối ưu hóa SGD.

Tổn thất BCE

Chúng tôi cũng sẽ sử dụng hàm mất mát (lỗi) L để đánh giá hiệu suất thuật toán của chúng tôi. Hãy nhớ rằng hàm tổn thất chỉ được áp dụng cho một mẫu đào tạo và hàm tổn thất thường được sử dụng nhất là một sai số bình phương. Tuy nhiên, hàm mất mát lỗi bình phương trong hồi quy logistic không phải là lựa chọn tốt nhất. Nó tạo ra một vấn đề tối ưu hóa không lồi và phương pháp tiếp cận gradient có thể không hội tụ một cách tối ưu. Chúng tôi sẽ áp dụng mức lỗ BCE để hy vọng đạt được mức tối ưu toàn cầu.

Mất BCE là viết tắt của Binary Cross-Entropy loss và thường được sử dụng trong các trường hợp phân loại nhị phân.

Cần lưu ý rằng khi sử dụng hàm mất BCE , đầu ra của nút phải nằm trong khoảng (0–1). Đối với điều này, chúng tôi sẽ cần sử dụng một trình tối ưu hóa thích hợp. Chúng tôi đã chọn SGD, hoặc Stochastic Gradient Descent, một trình tối ưu hóa được sử dụng thường xuyên. Các trình tối ưu hóa khác bao gồm Adam, Lars và những người khác.

Bạn nên cung cấp các thông số mô hình và tỷ lệ học tập làm đầu vào cho trình tối ưu hóa. SGD chọn ngẫu nhiên một điểm dữ liệu từ toàn bộ tập dữ liệu ở mỗi lần lặp để giảm thiểu tính toán một cách đáng kể.

Thông thường cũng thường lấy mẫu một số lượng nhỏ các điểm dữ liệu thay vì chỉ một điểm ở mỗi bước (thường được gọi là điểm gốc gradient theo lô nhỏ). Các nỗ lực hàng loạt nhỏ để cân bằng hiệu quả của gradient descent và tốc độ của SGD.

Tỷ lệ học

Tốc độ học tập là một siêu thông số có thể điều chỉnh được sử dụng trong đào tạo mạng nơ-ron với một chút giá trị dương, thường từ 0,0 đến 0,1. Tỷ lệ học tập thấp hơn cần nhiều kỷ nguyên đào tạo hơn vì có nhiều thay đổi nhỏ hơn về trọng số với mỗi lần cập nhật, trong khi tỷ lệ học tập xuất sắc hơn tạo ra những thay đổi nhanh chóng và yêu cầu ít kỷ lục đào tạo hơn.

Tỷ lệ học tập cao có thể khiến mô hình hội tụ quá nhanh vào một giải pháp kém, trong khi tỷ lệ học tập thấp có thể khiến quá trình bị đình trệ. Sử dụng lịch trình tốc độ học tập, bạn có thể thay đổi tốc độ học tập khi quá trình đào tạo diễn ra.

Nó điều chỉnh tốc độ học tập dựa trên lịch trình xác định trước, chẳng hạn như dựa trên thời gian, dựa trên bước hoặc theo cấp số nhân.

Chúng tôi có thể tạo một lịch trình tỷ lệ học tập để cập nhật tỷ lệ học tập trong suốt quá trình đào tạo dựa trên một quy tắc được xác định trước. Bộ lập lịch tốc độ học tập phổ biến nhất là giảm dần theo bước, làm giảm tốc độ học tập theo một tỷ lệ phần trăm nhất định sau một số kỷ nguyên đào tạo nhất định. Cuối cùng, chúng ta có thể nói rằng lịch trình tốc độ học tập là một cấu trúc được xác định trước để điều chỉnh tốc độ học tập qua các kỷ nguyên hoặc các lần lặp lại khi quá trình đào tạo diễn ra.

Sau đây là hai trong số các chiến lược phổ biến nhất cho lịch trình tỷ lệ học tập:

  • Giảm tốc độ học tập: Nó xảy ra khi chúng ta chọn một tốc độ học tập ban đầu và sau đó giảm dần tỷ lệ đó theo lịch trình.
  • Tốc độ học tập không đổi : Như tên của nó, chúng tôi đặt tốc độ học tập và không thay đổi nó trong suốt quá trình đào tạo.

Lưu ý: Tốc độ học tập là một siêu thông số cần được tinh chỉnh. Thay vì sử dụng tốc độ học không đổi, chúng ta có thể bắt đầu với giá trị LR cao hơn và sau đó giảm dần sau một số vòng cụ thể. Nó cho phép chúng ta có được sự hội tụ nhanh hơn lúc đầu trong khi giảm rủi ro vượt quá mức lỗ.

Trong PyTorch, chúng tôi có thể sử dụng nhiều bộ lập lịch từ optimgói. Bạn có thể theo liên kết này để xem cách bạn có thể điều chỉnh tốc độ học của mạng nơ-ron bằng PyTorch. Hãy xác định các tham số, mất mát và trình tối ưu hóa:

num_epochs = 500                                        
# Traning the model for large number of epochs to see better results  
learning_rate = 0.0001                               
criterion = nn.BCELoss()                                
# We are working on lgistic regression so using Binary Cross Entropy
optimizer = torch.optim.SGD(lr.parameters(), lr=learning_rate)      
# Using ADAM optimizer to find local minima   

Theo dõi quá trình đào tạo:

for epoch in range(num_epochs):
    y_pred = lr(X_train)
    loss = criterion(y_pred, y_train)             
    loss.backward()
    optimizer.step()
    optimizer.zero_grad()
    if (epoch+1) % 20 == 0:                                         
        # printing loss values on every 10 epochs to keep track
        print(f'epoch: {epoch+1}, loss = {loss.item():.4f}')
epoch: 20, loss = 0.8447
epoch: 40, loss = 0.8379
epoch: 60, loss = 0.8316
epoch: 80, loss = 0.8257
epoch: 100, loss = 0.8203
epoch: 120, loss = 0.8152
epoch: 140, loss = 0.8106
epoch: 160, loss = 0.8063
epoch: 180, loss = 0.8023
epoch: 200, loss = 0.7986
epoch: 220, loss = 0.7952
epoch: 240, loss = 0.7920
epoch: 260, loss = 0.7891
epoch: 280, loss = 0.7863
epoch: 300, loss = 0.7838
epoch: 320, loss = 0.7815
epoch: 340, loss = 0.7793
epoch: 360, loss = 0.7773
epoch: 380, loss = 0.7755
epoch: 400, loss = 0.7737
epoch: 420, loss = 0.7721
epoch: 440, loss = 0.7706
epoch: 460, loss = 0.7692
epoch: 480, loss = 0.7679
epoch: 500, loss = 0.7667

Ở đây, lần chuyển tiếp đầu tiên xảy ra. Tiếp theo, sự mất mát được tính toán. Khi loss.backward()được gọi, nó sẽ tính toán gradient mất mát liên quan đến trọng số (của lớp). Các trọng số sau đó được cập nhật bằng cách gọi optimizer.step(). Sau đó, các quả nặng phải được làm trống cho lần lặp tiếp theo. Vì vậy, zero_grad()phương thức được gọi là.

Đoạn mã trên in lỗ ở mỗi kỷ nguyên thứ 20.

Hiệu suất mô hình

Cuối cùng chúng ta hãy xem độ chính xác của mô hình:

with torch.no_grad():
    y_predicted = lr(X_test)
    y_predicted_cls = y_predicted.round()
    acc = y_predicted_cls.eq(y_test).sum() / float(y_test.shape[0])
    print(f'accuracy: {acc.item():.4f}')
accuracy: 0.5093

Chúng ta phải sử dụng torch.no_grad()ở đây. Mục đích là để bỏ qua tính toán gradient trên các trọng số. Vì vậy, bất kỳ thứ gì tôi đặt trong vòng lặp này sẽ không sửa đổi trọng số và do đó sẽ không làm gián đoạn quá trình nhân giống ngược.

Chúng tôi cũng có thể xem độ chính xác, thu hồi và điểm F1 bằng cách sử dụng báo cáo phân loại:

#classification report
from sklearn.metrics import classification_report
print(classification_report(y_test, y_predicted_cls))
              precision    recall  f1-score   support

         0.0       0.51      0.62      0.56       270
         1.0       0.51      0.40      0.45       270

    accuracy                           0.51       540
   macro avg       0.51      0.51      0.50       540
weighted avg       0.51      0.51      0.50       540

Hình dung Ma trận nhầm lẫn:

#confusion matrix
from sklearn.metrics import confusion_matrix
confusion_matrix = confusion_matrix(y_test, y_predicted_cls)
print(confusion_matrix)
[[168 102]
 [163 107]]

Sự kết luận

Lưu ý rằng mô hình của chúng tôi không hoạt động tốt trên tập dữ liệu tương đối phức tạp này. Mục tiêu của hướng dẫn này là chỉ cho bạn cách thực hiện hồi quy logistic trong PyTorch. Nếu bạn muốn có được độ chính xác tốt hơn và các số liệu khác, hãy cân nhắc tinh chỉnh các siêu tham số đào tạo, chẳng hạn như tăng số kỷ nguyên và tỷ lệ học tập hoặc thậm chí thêm một lớp nữa (tức là mạng nơ ron).

Nhận mã hoàn chỉnh trong sổ tay Colab này .

Nguồn https://www.thepythoncode.com

#python #pytorch #deeplearning #datascience #programming #developer 

Hồi quy logistic sử dụng PyTorch trong Python

Как выполнить алгоритм логистической регрессии в Python с помощью PyTorch

Логистическая регрессия с использованием PyTorch в Python

Узнайте, как выполнить алгоритм логистической регрессии с помощью платформы глубокого обучения PyTorch на примере набора данных об оттоке клиентов в Python.

Логистическая регрессия — это вероятностная модель, используемая для описания вероятности дискретных результатов при заданных входных переменных. Несмотря на название, логистическая регрессия — это модель классификации, а не модель регрессии. В двух словах, логистическая регрессия похожа на линейную регрессию, за исключением категоризации.

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

В этом руководстве основное внимание уделяется разработке модели логистической регрессии для прогнозирования оттока клиентов в PyTorch.

Что такое логистическая регрессия

Хотя название метода включает термин «регрессия», по сути, это контролируемый метод машинного обучения, предназначенный для решения проблем классификации. Это связано с использованием алгоритмом логистической функции, которая находится в диапазоне от 0 до 1.

В результате мы можем использовать логистическую регрессию для прогнозирования вероятности того, что одна переменная признака (X) принадлежит к определенной категории (Y).

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

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

Сигмовидная функция

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

На этом рисунке изображена S-образная кривая переменной для значений x в диапазоне от 0 до 1:

Сигмовидная функция

Источник: ДепозитФото

На протяжении большей части своей области сигмовидная функция имеет значения, очень близкие к 0 или 1. Из-за этого она подходит для использования в задачах бинарной классификации. Взгляните на рисунок ниже, чтобы увидеть, как представлена ​​модель логистической регрессии:

Модель логистической регрессии

 

Источник: датахакер

Как видите, мы начнем с вычисления выхода линейной функции z. Сигмовидная функция примет этот вывод z в качестве ввода. После этого для вычисленного z мы сгенерируем прогноз ŷ, который будет определять z. Если z — значительное положительное число, то ŷ будет близко к единице.

С другой стороны, если z имеет значительно отрицательное число, ŷ будет близко к нулю.

Следовательно, ŷ всегда будет находиться в диапазоне от 0 до 1. Использование порогового значения 0,5 — это простой способ категоризировать предсказание ŷ.

Если наш прогноз больше, чем 0,5, мы предполагаем, что ŷ равно 1.

В противном случае будем считать, что ŷ равно нулю. Когда мы применяем логистическую регрессию, наша цель состоит в том, чтобы попытаться вычислить параметры w и b так, чтобы ŷ стало достойной оценкой вероятности ŷ=1.

Описание данных

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

Связанный: Прогноз оттока клиентов: полное руководство по Python .

Давайте установим зависимости этого руководства:

$ pip install matplotlib numpy pandas scikit_learn==1.0.2 torch==1.10.1

Для автоматической загрузки набора данных мы можем использовать gdown:

$ pip install --upgrade gdown
$ gdown --id 12vfq3DYFId3bsXuNj_PhsACMzrLTfObs

Давайте начнем:

import pandas as pd
import numpy as np
import torch
import torch.nn as nn
from sklearn.utils import resample
from sklearn import preprocessing
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from warnings import filterwarnings
filterwarnings('ignore')

Давайте прочитаем данные, отбросим столбцы yearcustomer_idphone_noи посмотрим на форму данных:

#reading data
data = pd.read_csv("data_regression.csv")
##The dimension of the data is seen, and the output column is checked to see whether it is continuous or discrete. 
##In this case, the output is discrete, so a classification algorithm should be applied.
data = data.drop(["year", "customer_id", "phone_no"], axis=1)
print(data.shape)         # Lookiing the shape of the data
print(data.columns)       # Looking how many columns data has
data.dtypes  
data.head()

Обработка нулевого значения

Если у нас есть нулевые значения, нам нужно поработать над этим, прежде чем передавать их в нашу модель:

data.isnull().sum()
gender                    24
age                        0
no_of_days_subscribed      0
multi_screen               0
mail_subscribed            0
weekly_mins_watched        0
minimum_daily_mins         0
maximum_daily_mins         0
weekly_max_night_mins      0
videos_watched             0
maximum_days_inactive     28
customer_support_calls     0
churn                     35
dtype: int64
final_data = data.dropna()         # Dropping the null values

Выборка данных

Давайте выполним выборку данных, поскольку наши данные сильно несбалансированы. Upsampling и Downsampling использовались для классов меньшинства и большинства выходных данных. Наконец, данные объединяются для дальнейшей обработки:

final_data["churn"].value_counts()       
# let us see how many data is there in each class for deciding the sampling data number
0.0    1665
1.0     253
Name: churn, dtype: int64

Разделение данных

Во-первых, нам нужно разбить фрейм данных на отдельные классы перед выборкой:

data_majority = final_data[final_data['churn']==0] #class 0
data_minority = final_data[final_data['churn']==1] #class 1
#upsampling minority class
data_minority_upsampled = resample(data_minority, replace=True, n_samples=900, random_state=123) 
 #downsampling majority class
data_majority_downsampled = resample(data_majority, replace=False, n_samples=900, random_state=123)
#concanating both upsampled and downsampled class
## Data Concatenation:  Concatenating the dataframe after upsampling and downsampling 
#concanating both upsampled and downsampled class
data2 = pd.concat([data_majority_downsampled, data_minority_upsampled])
## Encoding Catagoricals:  We need to encode the categorical variables before feeding it to the model
data2[['gender', 'multi_screen', 'mail_subscribed']]
#label encoding categorical variables
label_encoder = preprocessing.LabelEncoder()
data2['gender']= label_encoder.fit_transform(data2['gender'])
data2['multi_screen']= label_encoder.fit_transform(data2['multi_screen'])
data2['mail_subscribed']= label_encoder.fit_transform(data2['mail_subscribed'])
## Lets now check again the distribution of the oputut class after sampling
data2["churn"].value_counts()
0.0    900
1.0    900
Name: churn, dtype: int64

В следующем коде мы имеем дело с масштабированием, разделением наборов для обучения и тестирования и разделением зависимых и независимых переменных:

#indenpendent variable 
X = data2.iloc[:,:-1]
## This X will be fed to the model to learn params 
#scaling the data
sc = StandardScaler()         # Bringing the mean to 0 and variance to 1, so as to have a non-noisy optimization
X = sc.fit_transform(X)
X = sc.transform(X)
## Keeping the output column in a separate dataframe
data2 = data2.sample(frac=1).reset_index(drop=True) ## Shuffle the data frame and reset index
n_samples, n_features = X.shape ## n_samples is the number of samples and n_features is the number of features
#output column
Y = data2["churn"]
#output column
Y = data2["churn"]
##Data Splitting: 
## The data is processed, so now we can split the data into train and test to train the model with training data and test it later from testing data.
#splitting data into train and test
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.30, random_state=42, stratify = Y)
print((y_train == 1).sum())
print((y_train == 0).sum())
630
630

Напечатаем тип поезда и тестовый набор:

print(type(X_train))
print(type(X_test))
print(type(y_train.values))
print(type(y_test.values))
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>

Преобразовывая их в тензоры по мере работы PyTorch, мы будем использовать torch.from_numpy()метод:

X_train = torch.from_numpy(X_train.astype(np.float32))
X_test = torch.from_numpy(X_test.astype(np.float32))
y_train = torch.from_numpy(y_train.values.astype(np.float32))
y_test = torch.from_numpy(y_test.values.astype(np.float32))

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

y_train = y_train.view(y_train.shape[0], 1)
y_test = y_test.view(y_test.shape[0], 1)

Функция активации

Мы используем функции активации для представления динамического взаимодействия в линейных данных. Здесь мы используем сигмовидную функцию активации. Мы выбрали сигмовидную функцию, поскольку она ограничивает значение от 0 до 1. Функции активации помогают ввести нелинейность в выходные данные нейрона, что повышает точность, эффективность вычислений и скорость сходимости.

Функции активации должны быть дифференцируемыми и быстро сходящимися по весам.

Недостатки сигмовидной:

  • Исчезающий градиент на этапе обратного распространения нейронной сети (особенно RNN).
  • Из-за своего экспоненциального характера он является дорогостоящим в вычислительном отношении.

Цитата из этой газеты : "Основной функцией активации, которая широко использовалась, является сигмовидная функция. Однако, когда был представлен выпрямительный линейный блок (ReLU) (Nair & Hinton, 2010), он вскоре стал лучшей заменой сигмовидной функции благодаря ее положительному влиянию на различные задачи машинного обучения. В то время как использование Sigmoid и работа с более мелкими слоями не вызывает никаких проблем, некоторые проблемы возникают, когда архитектура становится глубже, потому что производные члены, которые меньше 1, будут многократно умножаться друг на друга, что приводит к уменьшению значений. и меньше, пока градиент не стремится к нулю, следовательно, исчезает. С другой стороны, если значения больше единицы, происходит обратное: умножаемые числа становятся все больше и больше, пока не устремятся к бесконечности и не взорвут градиент. Хорошим решением было бы оставить значения равными 1, чтобы даже при умножении они не менялись. Это именно то, что делает ReLU: он имеет градиент 1 для положительных входных данных и 0 для отрицательных».

Построение модели: создание модели логистической регрессии в Pytorch

Ниже приведен код, ответственный за построение модели логистической регрессии в PyTorch:

#logistic regression class
class LogisticRegression(nn.Module):
    def __init__(self, n_input_features):
        super(LogisticRegression, self).__init__()
        self.linear = nn.Linear(n_input_features, 1)
    
    #sigmoid transformation of the input 
    def forward(self, x):
        y_pred = torch.sigmoid(self.linear(x))
        return y_pred

Вы должны объявить слои в вашей модели в __init__()методе. Мы использовали линейные слои, которые задаются с помощью torch.nnмодуля. Слою можно дать любое имя, например self.linear(в нашем случае). Я объявил один линейный слой, потому что это логистическая регрессия.

Синтаксис: torch.nn.Linear(in_features, out_features, bias=True)

Метод forward()отвечает за прямой проход/распространение. Ввод маршрутизируется через ранее установленный уровень, а вывод этого уровня отправляется через сигмовидную функцию активации.

Давайте инициализируем модель:

lr = LogisticRegression(n_features)

Компиляция модели

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

Потеря до нашей эры

Мы также будем использовать функцию потерь (ошибок) L для оценки производительности нашего алгоритма. Помните, что функция потерь применяется только к одной обучающей выборке, и наиболее часто используемая функция потерь представляет собой квадрат ошибки. Однако функция потери квадрата ошибки в логистической регрессии — не лучший вариант. Это создает задачу оптимизации, которая не является выпуклой, и подход градиентного спуска может не сходиться оптимально. Мы применим потерю BCE, чтобы, надеюсь, достичь глобального оптимума.

Потеря BCE означает потерю двоичной кросс-энтропии и часто используется в экземплярах двоичной классификации.

Стоит отметить, что при использовании функции потерь BCE выход узла должен быть между (0–1). Для этого нам потребуется использовать соответствующий оптимизатор. Мы выбрали SGD, или Stochastic Gradient Descent, часто используемый оптимизатор. Другие оптимизаторы включают Адама, Ларса и других.

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

Также обычно на каждом шаге отбирается небольшое количество точек данных, а не только одна (часто называемая мини-пакетным градиентным спуском). Мини-пакет пытается сбалансировать эффективность градиентного спуска и скорость SGD.

Скорость обучения

Скорость обучения — это регулируемый гиперпараметр, используемый при обучении нейронной сети, с небольшим положительным значением, часто от 0,0 до 0,1. Более низкая скорость обучения требует большего количества эпох обучения из-за более незначительных изменений весов при каждом обновлении, в то время как более высокая скорость обучения приводит к быстрым изменениям и требует меньшего количества эпох обучения.

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

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

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

Ниже приведены две наиболее распространенные стратегии для графиков скорости обучения:

  • Снижение скорости обучения: это происходит, когда мы выбираем начальную скорость обучения, а затем постепенно снижаем ее в соответствии с планировщиком.
  • Постоянная скорость обучения : как следует из названия, мы устанавливаем скорость обучения и не меняем ее на протяжении всего обучения.

Примечание. Скорость обучения — это гиперпараметр, который следует настроить. Вместо использования постоянной скорости обучения мы могли бы начать с более высокого значения LR, а затем постепенно уменьшать его после определенного количества раундов. Это позволяет нам иметь более быструю конвергенцию поначалу, снижая при этом риск превышения убытка.

В PyTorch мы можем использовать несколько планировщиков из optimпакета. Вы можете перейти по этой ссылке , чтобы увидеть, как можно настроить скорость обучения нейронной сети с помощью PyTorch. Определим параметры, потери и оптимизатор:

num_epochs = 500                                        
# Traning the model for large number of epochs to see better results  
learning_rate = 0.0001                               
criterion = nn.BCELoss()                                
# We are working on lgistic regression so using Binary Cross Entropy
optimizer = torch.optim.SGD(lr.parameters(), lr=learning_rate)      
# Using ADAM optimizer to find local minima   

Отслеживание тренировочного процесса:

for epoch in range(num_epochs):
    y_pred = lr(X_train)
    loss = criterion(y_pred, y_train)             
    loss.backward()
    optimizer.step()
    optimizer.zero_grad()
    if (epoch+1) % 20 == 0:                                         
        # printing loss values on every 10 epochs to keep track
        print(f'epoch: {epoch+1}, loss = {loss.item():.4f}')
epoch: 20, loss = 0.8447
epoch: 40, loss = 0.8379
epoch: 60, loss = 0.8316
epoch: 80, loss = 0.8257
epoch: 100, loss = 0.8203
epoch: 120, loss = 0.8152
epoch: 140, loss = 0.8106
epoch: 160, loss = 0.8063
epoch: 180, loss = 0.8023
epoch: 200, loss = 0.7986
epoch: 220, loss = 0.7952
epoch: 240, loss = 0.7920
epoch: 260, loss = 0.7891
epoch: 280, loss = 0.7863
epoch: 300, loss = 0.7838
epoch: 320, loss = 0.7815
epoch: 340, loss = 0.7793
epoch: 360, loss = 0.7773
epoch: 380, loss = 0.7755
epoch: 400, loss = 0.7737
epoch: 420, loss = 0.7721
epoch: 440, loss = 0.7706
epoch: 460, loss = 0.7692
epoch: 480, loss = 0.7679
epoch: 500, loss = 0.7667

Здесь происходит первый прямой проход. Далее подсчитываются убытки. При loss.backward()вызове он вычисляет градиент потерь по отношению к весам (слоя). Затем веса обновляются вызовом optimizer.step(). После этого веса должны быть освобождены для следующей итерации. Так zero_grad()называется метод.

Приведенный выше код печатает потери в каждую 20-ю эпоху.

Производительность модели

Наконец, посмотрим на точность модели:

with torch.no_grad():
    y_predicted = lr(X_test)
    y_predicted_cls = y_predicted.round()
    acc = y_predicted_cls.eq(y_test).sum() / float(y_test.shape[0])
    print(f'accuracy: {acc.item():.4f}')
accuracy: 0.5093

Мы должны использовать torch.no_grad()здесь. Цель состоит в том, чтобы исключить вычисление градиента по весам. Итак, все, что я добавляю в этот цикл, не изменит веса и, следовательно, не нарушит процесс обратного распространения.

Мы также можем увидеть точность, отзыв и оценку F1, используя отчет о классификации:

#classification report
from sklearn.metrics import classification_report
print(classification_report(y_test, y_predicted_cls))
              precision    recall  f1-score   support

         0.0       0.51      0.62      0.56       270
         1.0       0.51      0.40      0.45       270

    accuracy                           0.51       540
   macro avg       0.51      0.51      0.50       540
weighted avg       0.51      0.51      0.50       540

Визуализация матрицы путаницы:

#confusion matrix
from sklearn.metrics import confusion_matrix
confusion_matrix = confusion_matrix(y_test, y_predicted_cls)
print(confusion_matrix)
[[168 102]
 [163 107]]

Вывод

Обратите внимание, что наша модель плохо работает с этим относительно сложным набором данных. Цель этого руководства — показать вам, как выполнять логистическую регрессию в PyTorch. Если вы хотите получить более высокую точность и другие показатели, рассмотрите возможность точной настройки гиперпараметров обучения, таких как увеличение количества эпох и скорости обучения или даже добавление еще одного слоя (например, нейронной сети).

Получите полный код в этом блокноте Colab .

Оригинальный источник статьи на https://www.thepythoncode.com

So führen Sie einen logistischen Regressionsalgorithmus in Python mit PyTorch durch

Logistische Regression mit PyTorch in Python

Erfahren Sie, wie Sie einen logistischen Regressionsalgorithmus mit dem Deep-Learning-Framework PyTorch an einem Beispieldatensatz zur Kundenabwanderung in Python ausführen.

Die logistische Regression ist ein probabilistisches Modell, das verwendet wird, um die Wahrscheinlichkeit diskreter Ergebnisse bei gegebenen Eingabevariablen zu beschreiben. Trotz des Namens ist die logistische Regression ein Klassifikationsmodell, kein Regressionsmodell. Kurz gesagt, die logistische Regression ähnelt der linearen Regression mit Ausnahme der Kategorisierung.

Es berechnet die Wahrscheinlichkeit des Ergebnisses mit der Sigmoidfunktion. Aufgrund der nichtlinearen Transformation der Eingangsvariablen benötigt die logistische Regression keine linearen Korrelationen zwischen Eingangs- und Ausgangsvariablen.

Dieses Tutorial konzentriert sich auf die Entwicklung eines logistischen Regressionsmodells zur Prognose der Kundenabwanderung in PyTorch.

Was ist eine logistische Regression

Obwohl der Name der Methode den Begriff „Regression“ enthält, handelt es sich im Wesentlichen um eine Technik des überwachten maschinellen Lernens, die zur Behandlung von Klassifizierungsproblemen entwickelt wurde. Dies liegt an der Verwendung der logistischen Funktion durch den Algorithmus, die von 0 bis 1 reicht.

Daher können wir die logistische Regression verwenden, um die Wahrscheinlichkeit vorherzusagen, dass eine einzelne Merkmalsvariable (X) zu einer bestimmten Kategorie (Y) gehört.

Die logistische Regression hat viele Ähnlichkeiten mit der linearen Regression, obwohl die lineare Regression eher zur Vorhersage numerischer Werte als zu Klassifizierungsproblemen verwendet wird. Beide Strategien verwenden eine Linie, um die Zielvariable darzustellen.

Die lineare Regression passt eine Linie an die Daten an, um eine neue Menge vorherzusagen, während die logistische Regression eine Linie anpasst, um die beiden Klassen optimal zu trennen.

Die Sigmoidfunktion

Um zu verstehen, was die logistische Regression ist und wie sie funktioniert, müssen Sie zuerst die Sigmoidfunktion und die natürliche Logarithmusfunktion verstehen.

Dieses Bild zeigt den S-förmigen Verlauf einer Variablen für x-Werte im Bereich von 0 bis 1:

Sigmoid-Funktion

Quelle: DepositPhotos

Die Sigmoid-Funktion hat im größten Teil ihrer Domäne Werte, die extrem nahe bei 0 oder 1 liegen. Aus diesem Grund ist sie für die Verwendung bei binären Klassifizierungsproblemen geeignet. Schauen Sie sich die folgende Grafik an, um zu sehen, wie das logistische Regressionsmodell dargestellt wird:

Logistisches Regressionsmodell

 

Quelle: Datenhacker

Wie Sie sehen können, beginnen wir mit der Berechnung der Ausgabe einer linearen Funktion, z. Die Sigmoid-Funktion nimmt diese Ausgabe z als Eingabe. Anschließend generieren wir für das berechnete z die Vorhersage ŷ, die z bestimmt. Wenn z eine signifikante positive Zahl ist, dann wird ŷ nahe eins sein.

Wenn z andererseits eine deutlich negative Zahl hat, wird ŷ nahe Null sein.

Folglich liegt ŷ immer im Bereich von 0 bis 1. Die Verwendung eines Schwellenwerts von 0,5 ist eine einfache Technik, um die Vorhersage ŷ zu kategorisieren.

Wenn unsere Prognose umfangreicher als 0,5 ist, nehmen wir an, dass ŷ gleich 1 ist.

Andernfalls nehmen wir an, dass ŷ gleich Null ist. Wenn wir die logistische Regression anwenden, ist unser Ziel, zu versuchen, die Parameter w und b so zu berechnen, dass ŷ eine anständige Schätzung der Wahrscheinlichkeit von ŷ=1 wird.

Daten Beschreibung

Der in dieser Studie verwendete Datensatz liefert Informationen zur Kundenabwanderung basierend auf verschiedenen Variablen. Das Dataset hat 2000 Zeilen und 15 Merkmale, die zur Vorhersage der Abwanderung verwendet werden können. Es kann hier heruntergeladen  werden .

Siehe auch : Vorhersage der Kundenabwanderung: Ein vollständiger Leitfaden in Python .

Lassen Sie uns die Abhängigkeiten dieses Tutorials installieren:

$ pip install matplotlib numpy pandas scikit_learn==1.0.2 torch==1.10.1

Um den Datensatz automatisch herunterzuladen, können wir Folgendes verwenden gdown:

$ pip install --upgrade gdown
$ gdown --id 12vfq3DYFId3bsXuNj_PhsACMzrLTfObs

Lass uns anfangen:

import pandas as pd
import numpy as np
import torch
import torch.nn as nn
from sklearn.utils import resample
from sklearn import preprocessing
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from warnings import filterwarnings
filterwarnings('ignore')

Lassen Sie uns die Daten lesen, die Spalten yearcustomer_idphone_nolöschen und uns die Form der Daten ansehen:

#reading data
data = pd.read_csv("data_regression.csv")
##The dimension of the data is seen, and the output column is checked to see whether it is continuous or discrete. 
##In this case, the output is discrete, so a classification algorithm should be applied.
data = data.drop(["year", "customer_id", "phone_no"], axis=1)
print(data.shape)         # Lookiing the shape of the data
print(data.columns)       # Looking how many columns data has
data.dtypes  
data.head()

Behandlung von Nullwerten

Wenn wir Nullwerte haben, müssen wir daran arbeiten, bevor wir sie unserem Modell zuführen:

data.isnull().sum()
gender                    24
age                        0
no_of_days_subscribed      0
multi_screen               0
mail_subscribed            0
weekly_mins_watched        0
minimum_daily_mins         0
maximum_daily_mins         0
weekly_max_night_mins      0
videos_watched             0
maximum_days_inactive     28
customer_support_calls     0
churn                     35
dtype: int64
final_data = data.dropna()         # Dropping the null values

Datenabtastung

Lassen Sie uns die Daten abtasten, da unsere Daten stark unausgewogen sind. Upsampling und Downsampling wurden für Minderheits- und Mehrheitsklassen der Ausgabe verwendet. Abschließend werden die Daten zur weiteren Verarbeitung verkettet:

final_data["churn"].value_counts()       
# let us see how many data is there in each class for deciding the sampling data number
0.0    1665
1.0     253
Name: churn, dtype: int64

Datenaufteilung

Zuerst müssen wir den Datenrahmen vor dem Sampling in separate Klassen aufteilen:

data_majority = final_data[final_data['churn']==0] #class 0
data_minority = final_data[final_data['churn']==1] #class 1
#upsampling minority class
data_minority_upsampled = resample(data_minority, replace=True, n_samples=900, random_state=123) 
 #downsampling majority class
data_majority_downsampled = resample(data_majority, replace=False, n_samples=900, random_state=123)
#concanating both upsampled and downsampled class
## Data Concatenation:  Concatenating the dataframe after upsampling and downsampling 
#concanating both upsampled and downsampled class
data2 = pd.concat([data_majority_downsampled, data_minority_upsampled])
## Encoding Catagoricals:  We need to encode the categorical variables before feeding it to the model
data2[['gender', 'multi_screen', 'mail_subscribed']]
#label encoding categorical variables
label_encoder = preprocessing.LabelEncoder()
data2['gender']= label_encoder.fit_transform(data2['gender'])
data2['multi_screen']= label_encoder.fit_transform(data2['multi_screen'])
data2['mail_subscribed']= label_encoder.fit_transform(data2['mail_subscribed'])
## Lets now check again the distribution of the oputut class after sampling
data2["churn"].value_counts()
0.0    900
1.0    900
Name: churn, dtype: int64

Im folgenden Code befassen wir uns mit der Skalierung, der Aufteilung der Trainings- und Testdatensätze und der Trennung von abhängigen und unabhängigen Variablen:

#indenpendent variable 
X = data2.iloc[:,:-1]
## This X will be fed to the model to learn params 
#scaling the data
sc = StandardScaler()         # Bringing the mean to 0 and variance to 1, so as to have a non-noisy optimization
X = sc.fit_transform(X)
X = sc.transform(X)
## Keeping the output column in a separate dataframe
data2 = data2.sample(frac=1).reset_index(drop=True) ## Shuffle the data frame and reset index
n_samples, n_features = X.shape ## n_samples is the number of samples and n_features is the number of features
#output column
Y = data2["churn"]
#output column
Y = data2["churn"]
##Data Splitting: 
## The data is processed, so now we can split the data into train and test to train the model with training data and test it later from testing data.
#splitting data into train and test
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.30, random_state=42, stratify = Y)
print((y_train == 1).sum())
print((y_train == 0).sum())
630
630

Lassen Sie uns den Zugtyp und das Testset drucken:

print(type(X_train))
print(type(X_test))
print(type(y_train.values))
print(type(y_test.values))
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>

Um sie in Tensoren umzuwandeln, während PyTorch weiterarbeitet, verwenden wir die torch.from_numpy()Methode:

X_train = torch.from_numpy(X_train.astype(np.float32))
X_test = torch.from_numpy(X_test.astype(np.float32))
y_train = torch.from_numpy(y_train.values.astype(np.float32))
y_test = torch.from_numpy(y_test.values.astype(np.float32))

Wenn wir den Ausgabevektor Y als Spaltenvektor für Matrixmultiplikationen verwenden, führen wir diese Änderung mit der Ansichtsoperation durch, wie im folgenden Code gezeigt:

y_train = y_train.view(y_train.shape[0], 1)
y_test = y_test.view(y_test.shape[0], 1)

Aktivierungsfunktion

Wir verwenden Aktivierungsfunktionen, um die dynamische Interaktion in linearen Daten darzustellen. Hier verwenden wir eine Sigmoid-Aktivierungsfunktion. Wir haben die Sigmoid-Funktion ausgewählt, da sie den Wert von 0 auf 1 begrenzt. Aktivierungsfunktionen helfen dabei, Nichtlinearität in die Ausgabe eines Neurons einzuführen, was die Genauigkeit, Recheneffizienz und Konvergenzgeschwindigkeit verbessert.

Aktivierungsfunktionen sollten bezüglich der Gewichte differenzierbar und schnell konvergierend sein.

Sigmoid Nachteile:

  • Verschwindender Gradient während der Backpropagation-Phase eines neuronalen Netzwerks (insbesondere RNNs).
  • Wegen seiner exponentiellen Natur ist es rechenintensiv.

Um aus diesem Papier zu zitieren : „Die wichtigste Aktivierungsfunktion, die weit verbreitet war, ist die Sigmoid-Funktion. Als jedoch die Rectifier Linear Unit (ReLU) (Nair & Hinton, 2010) eingeführt wurde, wurde sie aufgrund ihrer positiven Auswirkungen auf die verschiedenen maschinellen Lernaufgaben bald zu einem besseren Ersatz für die Sigmoid-Funktion. Während die Verwendung von Sigmoid und die Arbeit an flacheren Schichten kein Problem darstellen, treten einige Probleme auf, wenn die Architektur tiefer wird, da die Ableitungsterme, die kleiner als 1 sind, so oft miteinander multipliziert werden, dass die Werte kleiner werden. und kleiner, bis der Gradient gegen Null tendiert und somit verschwindet. Auf der anderen Seite, wenn die Werte größer als eins sind, passiert das Gegenteil, indem Zahlen multipliziert werden, die immer größer werden, bis sie gegen unendlich streben und den Gradienten explodieren lassen. Eine gute Lösung wäre, die Werte auf 1 zu belassen, damit sie sich auch beim Multiplizieren nicht ändern. Genau das macht ReLU: Es hat einen Gradienten von 1 für positive Eingänge und 0 für negative.

Modellerstellung: Erstellen eines logistischen Regressionsmodells in Pytorch

Nachfolgend finden Sie den verantwortlichen Code zum Erstellen des logistischen Regressionsmodells in PyTorch:

#logistic regression class
class LogisticRegression(nn.Module):
    def __init__(self, n_input_features):
        super(LogisticRegression, self).__init__()
        self.linear = nn.Linear(n_input_features, 1)
    
    #sigmoid transformation of the input 
    def forward(self, x):
        y_pred = torch.sigmoid(self.linear(x))
        return y_pred

Sie müssen die Schichten in Ihrem Modell in der __init__()Methode deklarieren. Wir haben lineare Schichten verwendet, die mit dem torch.nnModul angegeben werden. Der Ebene kann ein beliebiger Name gegeben werden, z. B. self.linear(in unserem Fall). Ich habe eine lineare Schicht deklariert, weil das eine logistische Regression ist.

Die Syntax lautet: torch.nn.Linear(in_features, out_features, bias=True)

Das forward()Verfahren ist für die Durchführung des Vorwärtsdurchlaufs/der Vorwärtsausbreitung zuständig. Die Eingabe wird über die zuvor eingerichtete Schicht geleitet, und die Ausgabe dieser Schicht wird über die Sigmoid-Aktivierungsfunktion gesendet.

Lassen Sie uns das Modell initialisieren:

lr = LogisticRegression(n_features)

Modellkompilierung

Lassen Sie uns die Anzahl der Epochen und die Lernrate definieren, die wir für unser Trainingsmodell wünschen. Da die Daten binär sind, verwenden wir Binary Cross Entropy als Verlustfunktion, die verwendet wird, um das Modell mit einem SGD-Optimierer zu optimieren.

BCE-Verlust

Wir werden auch die Verlust-(Fehler-)Funktion L verwenden, um die Leistung unseres Algorithmus zu bewerten. Denken Sie daran, dass die Verlustfunktion nur auf eine Trainingsprobe angewendet wird und die am häufigsten verwendete Verlustfunktion ein quadratischer Fehler ist. Die quadrierte Fehlerverlustfunktion in der logistischen Regression ist jedoch nicht die beste Option. Es erzeugt ein Optimierungsproblem, das nicht konvex ist, und der Gradientenabstiegsansatz kann möglicherweise nicht optimal konvergieren. Wir werden den BCE-Verlust anwenden, um hoffentlich das globale Optimum zu erreichen.

BCE-Verlust steht für Binary Cross-Entropy Loss und wird häufig in binären Klassifikationsinstanzen verwendet.

Beachten Sie, dass bei Verwendung der BCE-Verlustfunktion die Ausgabe des Knotens zwischen (0 und 1) liegen sollte. Dazu müssen wir einen geeigneten Optimierer einsetzen. Wir haben uns für SGD oder Stochastic Gradient Descent entschieden, einen regelmäßig verwendeten Optimierer. Andere Optimierer sind Adam, Lars und andere.

Sie sollten Modellparameter und Lernrate als Eingabe für den Optimierer bereitstellen. SGD wählt bei jeder Iteration zufällig einen Datenpunkt aus dem gesamten Datensatz aus, um die Berechnungen drastisch zu minimieren.

Es ist auch üblich, bei jedem Schritt eine winzige Anzahl von Datenpunkten statt nur einen abzutasten (oft als Mini-Batch-Gradientenabstieg bezeichnet). Mini-Batch versucht, die Effizienz des Gradientenabstiegs und die SGD-Geschwindigkeit auszugleichen.

Lernrate

Die Lernrate ist ein einstellbarer Hyperparameter, der beim Training neuronaler Netze mit einem leicht positiven Wert verwendet wird, häufig zwischen 0,0 und 0,1. Niedrigere Lernraten erfordern mehr Trainingsepochen aufgrund der kleineren Änderungen in den Gewichten bei jeder Aktualisierung, während hervorragendere Lernraten schnelle Änderungen erzeugen und weniger Trainingsepochen erfordern.

Eine hohe Lernrate kann dazu führen, dass das Modell zu schnell auf eine schlechte Lösung konvergiert, während eine niedrige Lernrate dazu führen kann, dass der Prozess ins Stocken gerät. Mithilfe der Lernratenpläne können Sie die Lernrate ändern, während das Training fortschreitet.

Es passt die Lernrate basierend auf einem vordefinierten Zeitplan an, z. B. zeitbasiert, schrittweise oder exponentiell.

Wir können einen Lernratenplan erstellen, um die Lernrate während des Trainings basierend auf einer vordefinierten Regel zu aktualisieren. Der gebräuchlichste Lernraten-Scheduler ist ein Stufenabfall, der die Lernrate nach einer bestimmten Anzahl von Trainingsepochen um einen bestimmten Prozentsatz reduziert. Schließlich können wir sagen, dass ein Lernratenplan eine vorgegebene Struktur zum Anpassen der Lernrate über Epochen oder Iterationen hinweg ist, wenn das Training stattfindet.

Im Folgenden sind zwei der am weitesten verbreiteten Strategien für Lernratenpläne aufgeführt:

  • Lernratenabfall : Er tritt auf, wenn wir eine anfängliche Lernrate wählen und sie dann in Übereinstimmung mit einem Planer schrittweise senken.
  • Konstante Lernrate : Wie der Name schon sagt, legen wir eine Lernrate fest und verändern diese während des gesamten Trainings nicht.

Hinweis: Die Lernrate ist ein Hyperparameter, der optimiert werden sollte. Anstatt eine konstante Lernrate zu verwenden, könnten wir mit einem höheren LR-Wert beginnen und ihn dann nach einer bestimmten Anzahl von Runden allmählich verringern. Es ermöglicht uns zunächst eine schnellere Konvergenz und verringert gleichzeitig das Risiko, den Verlust zu überschreiten.

In PyTorch können wir mehrere Planer aus dem optimPaket verwenden. Sie können diesem Link folgen , um zu sehen, wie Sie die Lernrate eines neuronalen Netzwerks mit PyTorch anpassen können. Lassen Sie uns die Parameter, den Verlust und den Optimierer definieren:

num_epochs = 500                                        
# Traning the model for large number of epochs to see better results  
learning_rate = 0.0001                               
criterion = nn.BCELoss()                                
# We are working on lgistic regression so using Binary Cross Entropy
optimizer = torch.optim.SGD(lr.parameters(), lr=learning_rate)      
# Using ADAM optimizer to find local minima   

Tracking des Trainingsprozesses:

for epoch in range(num_epochs):
    y_pred = lr(X_train)
    loss = criterion(y_pred, y_train)             
    loss.backward()
    optimizer.step()
    optimizer.zero_grad()
    if (epoch+1) % 20 == 0:                                         
        # printing loss values on every 10 epochs to keep track
        print(f'epoch: {epoch+1}, loss = {loss.item():.4f}')
epoch: 20, loss = 0.8447
epoch: 40, loss = 0.8379
epoch: 60, loss = 0.8316
epoch: 80, loss = 0.8257
epoch: 100, loss = 0.8203
epoch: 120, loss = 0.8152
epoch: 140, loss = 0.8106
epoch: 160, loss = 0.8063
epoch: 180, loss = 0.8023
epoch: 200, loss = 0.7986
epoch: 220, loss = 0.7952
epoch: 240, loss = 0.7920
epoch: 260, loss = 0.7891
epoch: 280, loss = 0.7863
epoch: 300, loss = 0.7838
epoch: 320, loss = 0.7815
epoch: 340, loss = 0.7793
epoch: 360, loss = 0.7773
epoch: 380, loss = 0.7755
epoch: 400, loss = 0.7737
epoch: 420, loss = 0.7721
epoch: 440, loss = 0.7706
epoch: 460, loss = 0.7692
epoch: 480, loss = 0.7679
epoch: 500, loss = 0.7667

Hier findet der erste Vorwärtspass statt. Als nächstes wird der Verlust berechnet. Wenn loss.backward()es aufgerufen wird, berechnet es den Verlustgradienten in Bezug auf die Gewichte (der Schicht). Die Gewichtungen werden dann durch Aufrufen von aktualisiert optimizer.step(). Danach müssen die Gewichte für die nächste Iteration geleert werden. Die zero_grad()Methode wird also aufgerufen.

Der obige Code gibt den Verlust in jeder 20. Epoche aus.

Modellleistung

Lassen Sie uns endlich die Modellgenauigkeit sehen:

with torch.no_grad():
    y_predicted = lr(X_test)
    y_predicted_cls = y_predicted.round()
    acc = y_predicted_cls.eq(y_test).sum() / float(y_test.shape[0])
    print(f'accuracy: {acc.item():.4f}')
accuracy: 0.5093

Wir müssen torch.no_grad()hier verwenden. Ziel ist es, die Gradientenberechnung über den Gewichten wegzulassen. Alles, was ich in diese Schleife einfüge, ändert also nicht die Gewichtungen und stört somit nicht den Backpropagation-Prozess.

Mithilfe des Klassifizierungsberichts können wir auch die Genauigkeit, den Abruf und die F1-Punktzahl anzeigen:

#classification report
from sklearn.metrics import classification_report
print(classification_report(y_test, y_predicted_cls))
              precision    recall  f1-score   support

         0.0       0.51      0.62      0.56       270
         1.0       0.51      0.40      0.45       270

    accuracy                           0.51       540
   macro avg       0.51      0.51      0.50       540
weighted avg       0.51      0.51      0.50       540

Visualisierung der Verwirrungsmatrix:

#confusion matrix
from sklearn.metrics import confusion_matrix
confusion_matrix = confusion_matrix(y_test, y_predicted_cls)
print(confusion_matrix)
[[168 102]
 [163 107]]

Fazit

Beachten Sie, dass unser Modell bei diesem relativ komplexen Datensatz nicht gut abschneidet. Das Ziel dieses Tutorials ist es, Ihnen zu zeigen, wie Sie eine logistische Regression in PyTorch durchführen. Wenn Sie eine bessere Genauigkeit und andere Metriken erhalten möchten, sollten Sie eine Feinabstimmung der Trainings-Hyperparameter in Betracht ziehen, z. B. die Anzahl der Epochen und die Lernrate erhöhen oder sogar eine weitere Ebene (dh ein neuronales Netzwerk) hinzufügen.

Holen Sie sich den vollständigen Code in diesem Colab-Notizbuch .

Quelle des Originalartikels unter https://www.thepythoncode.com

Como executar o algoritmo de regressão logística em Python com PyTorch

Regressão logística usando PyTorch em Python

Saiba como executar o algoritmo de regressão logística usando a estrutura de aprendizado profundo do PyTorch em um conjunto de dados de exemplo de rotatividade de clientes em Python.

A regressão logística é um modelo probabilístico usado para descrever a probabilidade de resultados discretos dadas as variáveis ​​de entrada. Apesar do nome, a regressão logística é um modelo de classificação, não um modelo de regressão. Em poucas palavras, a regressão logística é semelhante à regressão linear, exceto pela categorização.

Ele calcula a probabilidade do resultado usando a função sigmóide. Por causa da transformação não linear da variável de entrada, a regressão logística não precisa de correlações lineares entre as variáveis ​​de entrada e saída.

Este tutorial se concentra no desenvolvimento de um modelo de regressão logística para prever a perda de clientes no PyTorch.

O que é uma regressão logística

Embora o nome do método inclua o termo "regressão", é essencialmente uma técnica de aprendizado de máquina supervisionada projetada para lidar com problemas de classificação. Isso se deve ao uso do algoritmo da função logística, que varia de 0 a 1.

Como resultado, podemos usar a regressão logística para prever a probabilidade de uma única variável de característica (X) pertencer a uma categoria específica (Y).

A regressão logística tem muitas semelhanças com a regressão linear, embora a regressão linear seja usada para prever valores numéricos em vez de problemas de classificação. Ambas as estratégias usam uma linha para representar a variável de destino.

A regressão linear ajusta uma linha aos dados para prever uma nova quantidade, enquanto a regressão logística ajusta uma linha para separar as duas classes de maneira ideal.

A função sigmóide

Para entender o que é regressão logística e como ela funciona, você deve primeiro entender a função sigmóide e a função logarítmica natural.

Esta imagem mostra a curva em forma de S de uma variável para valores de x variando de 0 a 1:

Função sigmóide

Fonte: DepositPhotos

Na maior parte de seu domínio, a função sigmóide possui valores extremamente próximos de 0 ou 1. Por isso, é apropriada para uso em problemas de classificação binária. Dê uma olhada no gráfico abaixo para ver como o modelo de regressão logística é representado:

Modelo de regressão logística

 

Fonte: hacker de dados

Como você pode ver, vamos começar calculando a saída de uma função linear, z. A função sigmóide tomará esta saída z como entrada. Em seguida, para z calculado, geraremos a previsão ŷ, que z determinará. Se z for um número positivo significativo, então ŷ estará próximo de um.

Por outro lado, se z tiver um número significativamente negativo, ŷ será próximo de zero.

Conseqüentemente, ŷ sempre estará no intervalo de 0 a 1. Usar um valor limite de 0,5 é uma técnica direta para categorizar a previsão ŷ.

Se nossa previsão for mais extensa que 0,5, assumimos que ŷ é igual a 1.

Caso contrário, vamos supor que ŷ é igual a zero. Quando aplicamos a regressão logística, nosso objetivo é tentar calcular os parâmetros w e b de forma que ŷ se torne uma estimativa decente da probabilidade de ŷ=1.

descrição de dados

O conjunto de dados usado neste estudo fornece informações sobre a perda de clientes com base em várias variáveis. O conjunto de dados tem 2.000 linhas e 15 recursos que podem ser usados ​​para prever o churn. Ele pode ser baixado  aqui .

Relacionado: Previsão de perda de clientes: um guia completo em Python .

Vamos instalar as dependências deste tutorial:

$ pip install matplotlib numpy pandas scikit_learn==1.0.2 torch==1.10.1

Para baixar automaticamente o conjunto de dados, podemos usar gdown:

$ pip install --upgrade gdown
$ gdown --id 12vfq3DYFId3bsXuNj_PhsACMzrLTfObs

Vamos começar:

import pandas as pd
import numpy as np
import torch
import torch.nn as nn
from sklearn.utils import resample
from sklearn import preprocessing
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from warnings import filterwarnings
filterwarnings('ignore')

Vamos ler os dados, soltar as colunas yearcustomer_idphone_noe observar a forma dos dados:

#reading data
data = pd.read_csv("data_regression.csv")
##The dimension of the data is seen, and the output column is checked to see whether it is continuous or discrete. 
##In this case, the output is discrete, so a classification algorithm should be applied.
data = data.drop(["year", "customer_id", "phone_no"], axis=1)
print(data.shape)         # Lookiing the shape of the data
print(data.columns)       # Looking how many columns data has
data.dtypes  
data.head()

Tratamento de valor nulo

Se tivermos valores nulos, precisamos trabalhar nisso antes de alimentá-lo ao nosso modelo:

data.isnull().sum()
gender                    24
age                        0
no_of_days_subscribed      0
multi_screen               0
mail_subscribed            0
weekly_mins_watched        0
minimum_daily_mins         0
maximum_daily_mins         0
weekly_max_night_mins      0
videos_watched             0
maximum_days_inactive     28
customer_support_calls     0
churn                     35
dtype: int64
final_data = data.dropna()         # Dropping the null values

Amostragem de dados

Vamos amostrar os dados, pois nossos dados são altamente desequilibrados. Upsampling e Downsampling foram usados ​​para classes minoritárias e majoritárias da saída. Finalmente, os dados são concatenados para processamento adicional:

final_data["churn"].value_counts()       
# let us see how many data is there in each class for deciding the sampling data number
0.0    1665
1.0     253
Name: churn, dtype: int64

Divisão de dados

Primeiro, precisamos dividir o quadro de dados em classes separadas antes da amostragem:

data_majority = final_data[final_data['churn']==0] #class 0
data_minority = final_data[final_data['churn']==1] #class 1
#upsampling minority class
data_minority_upsampled = resample(data_minority, replace=True, n_samples=900, random_state=123) 
 #downsampling majority class
data_majority_downsampled = resample(data_majority, replace=False, n_samples=900, random_state=123)
#concanating both upsampled and downsampled class
## Data Concatenation:  Concatenating the dataframe after upsampling and downsampling 
#concanating both upsampled and downsampled class
data2 = pd.concat([data_majority_downsampled, data_minority_upsampled])
## Encoding Catagoricals:  We need to encode the categorical variables before feeding it to the model
data2[['gender', 'multi_screen', 'mail_subscribed']]
#label encoding categorical variables
label_encoder = preprocessing.LabelEncoder()
data2['gender']= label_encoder.fit_transform(data2['gender'])
data2['multi_screen']= label_encoder.fit_transform(data2['multi_screen'])
data2['mail_subscribed']= label_encoder.fit_transform(data2['mail_subscribed'])
## Lets now check again the distribution of the oputut class after sampling
data2["churn"].value_counts()
0.0    900
1.0    900
Name: churn, dtype: int64

No código a seguir, lidamos com dimensionamento, divisão dos conjuntos de treinamento e teste e separação de variáveis ​​dependentes e independentes:

#indenpendent variable 
X = data2.iloc[:,:-1]
## This X will be fed to the model to learn params 
#scaling the data
sc = StandardScaler()         # Bringing the mean to 0 and variance to 1, so as to have a non-noisy optimization
X = sc.fit_transform(X)
X = sc.transform(X)
## Keeping the output column in a separate dataframe
data2 = data2.sample(frac=1).reset_index(drop=True) ## Shuffle the data frame and reset index
n_samples, n_features = X.shape ## n_samples is the number of samples and n_features is the number of features
#output column
Y = data2["churn"]
#output column
Y = data2["churn"]
##Data Splitting: 
## The data is processed, so now we can split the data into train and test to train the model with training data and test it later from testing data.
#splitting data into train and test
X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.30, random_state=42, stratify = Y)
print((y_train == 1).sum())
print((y_train == 0).sum())
630
630

Vamos imprimir o tipo de trem e conjunto de teste:

print(type(X_train))
print(type(X_test))
print(type(y_train.values))
print(type(y_test.values))
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>
<class 'numpy.ndarray'>

Convertendo-os em tensores à medida que o PyTorch funciona, usaremos o torch.from_numpy()método:

X_train = torch.from_numpy(X_train.astype(np.float32))
X_test = torch.from_numpy(X_test.astype(np.float32))
y_train = torch.from_numpy(y_train.values.astype(np.float32))
y_test = torch.from_numpy(y_test.values.astype(np.float32))

Fazendo o vetor de saída Y como um vetor coluna para multiplicações de matrizes, realizamos essa alteração usando a operação de visualização conforme mostrado no código abaixo:

y_train = y_train.view(y_train.shape[0], 1)
y_test = y_test.view(y_test.shape[0], 1)

Função de ativação

Usamos funções de ativação para representar a interação dinâmica em dados lineares. Aqui, utilizamos uma função de ativação sigmóide. Escolhemos a função sigmóide, pois ela limitará o valor de 0 a 1. As funções de ativação ajudam a introduzir não linearidade na saída de um neurônio, o que melhora a precisão, a eficiência computacional e a velocidade de convergência.

As funções de ativação devem ser diferenciáveis ​​e convergirem rapidamente em relação aos pesos.

Desvantagens do sigmóide:

  • Vanishing Gradient durante o estágio de retropropagação de uma rede neural (especialmente RNNs).
  • Devido à sua natureza exponencial, é computacionalmente caro.

Para citar este artigo : "A principal função de ativação que foi amplamente utilizada é a função Sigmóide. No entanto, quando o Rectifier Linear Unit (ReLU) (Nair & Hinton, 2010) foi introduzido, logo se tornou um substituto melhor para a função Sigmoid devido ao seu impacto positivo nas diferentes tarefas de aprendizado de máquina. Embora usar Sigmoid e trabalhar em camadas mais rasas não dê nenhum problema, alguns problemas surgem quando a arquitetura se torna mais profunda porque os termos derivados que são menores que 1 serão multiplicados um pelo outro muitas vezes que os valores se tornarão menores. e menor até que o gradiente tenda a zero, portanto, desaparecendo. Por outro lado, se os valores forem maiores que um, acontece o contrário, com os números sendo multiplicados cada vez maiores até tenderem ao infinito e explodirem o gradiente. Uma boa solução seria manter os valores em 1 para que, mesmo quando multiplicados, eles não mudem. Isso é exatamente o que o ReLU faz: tem gradiente 1 para entradas positivas e 0 para entradas negativas.."

Construção de modelo: criando modelo de regressão logística no Pytorch

Abaixo está o código responsável pela construção do modelo de Regressão Logística no PyTorch:

#logistic regression class
class LogisticRegression(nn.Module):
    def __init__(self, n_input_features):
        super(LogisticRegression, self).__init__()
        self.linear = nn.Linear(n_input_features, 1)
    
    #sigmoid transformation of the input 
    def forward(self, x):
        y_pred = torch.sigmoid(self.linear(x))
        return y_pred

Você deve declarar as camadas em seu modelo no __init__()método. Usamos camadas lineares, que são especificadas usando o torch.nnmódulo. A camada pode receber qualquer nome, como self.linear(no nosso caso). Eu declarei uma camada linear porque isso é regressão logística.

A sintaxe é: torch.nn.Linear(in_features, out_features, bias=True)

O forward()método é responsável por conduzir a passagem/propagação para frente. A entrada é roteada pela camada previamente estabelecida, e a saída dessa camada é enviada pela função de ativação sigmóide.

Vamos inicializar o modelo:

lr = LogisticRegression(n_features)

Compilação de modelos

Vamos definir o número de épocas e a taxa de aprendizado que queremos nosso modelo para treinamento. Como os dados são binários, usaremos Binary Cross Entropy como a função de perda usada para otimizar o modelo usando um otimizador SGD.

Perda BCE

Também utilizaremos a função de perda (erro) L para avaliar o desempenho do nosso algoritmo. Lembre-se de que a função de perda é aplicada apenas a uma amostra de treinamento, e a função de perda mais comumente empregada é um erro quadrado. No entanto, a função de perda de erro ao quadrado na regressão logística não é a melhor opção. Ele produz um problema de otimização que não é convexo, e a abordagem gradiente descendente pode não convergir de maneira ótima. Vamos aplicar a perda BCE para alcançar o ótimo global.

A perda de BCE significa perda de entropia cruzada binária e é frequentemente usada em instâncias de classificação binária.

Vale a pena notar que ao usar a função de perda BCE , a saída do nó deve estar entre (0–1). Para isso, precisaremos empregar um otimizador apropriado. Escolhemos o SGD, ou Stochastic Gradient Descent, um otimizador usado regularmente. Outros otimizadores incluem Adam, Lars e outros.

Você deve fornecer parâmetros de modelo e taxa de aprendizado como entrada para o otimizador. O SGD escolhe um ponto de dados aleatoriamente de todo o conjunto de dados em cada iteração para minimizar os cálculos drasticamente.

Também é comum amostrar um pequeno número de pontos de dados em vez de apenas um em cada etapa (geralmente chamado de descida de gradiente de mini-lote). O mini-lote tenta equilibrar a eficiência da descida do gradiente e a velocidade do SGD.

Taxa de Aprendizagem

A taxa de aprendizado é um hiperparâmetro ajustável usado no treinamento de redes neurais com um pouco de valor positivo, geralmente de 0,0 a 0,1. Taxas de aprendizado mais baixas precisam de mais épocas de treinamento por causa das mudanças menores nos pesos com cada atualização, enquanto taxas de aprendizado mais excelentes produzem mudanças rápidas e exigem menos épocas de treinamento.

Uma alta taxa de aprendizado pode levar o modelo a convergir muito rapidamente em uma solução ruim, enquanto uma baixa taxa de aprendizado pode fazer com que o processo pare. Usando as programações de taxa de aprendizado, você pode alterar a taxa de aprendizado à medida que o treinamento avança.

Ele ajusta a taxa de aprendizado com base em um cronograma predefinido, como baseado em tempo, baseado em etapas ou exponencial.

Podemos criar um cronograma de taxa de aprendizado para atualizar a taxa de aprendizado ao longo do treinamento com base em uma regra predefinida. O escalonador de taxa de aprendizado mais comum é um step decay, que reduz a taxa de aprendizado em uma certa porcentagem após um certo número de épocas de treinamento. Finalmente, podemos dizer que um cronograma de taxa de aprendizado é uma estrutura predeterminada para ajustar a taxa de aprendizado entre épocas ou iterações à medida que o treinamento ocorre.

A seguir estão duas das estratégias mais prevalentes para programações de taxa de aprendizado:

  • Decaimento da taxa de aprendizado: ocorre quando escolhemos uma taxa de aprendizado inicial e a reduzimos progressivamente de acordo com um escalonador.
  • Taxa de aprendizado constante : Como o nome indica, definimos uma taxa de aprendizado e não a variamos ao longo do treinamento.

Nota: A taxa de aprendizado é um hiperparâmetro que deve ser ajustado. Em vez de usar uma taxa de aprendizado constante, podemos começar com um valor mais alto de LR e diminuí-lo gradualmente após um número específico de rodadas. Isso nos permite ter uma convergência mais rápida no início, reduzindo os riscos de ultrapassar a perda.

No PyTorch, podemos utilizar vários agendadores do optimpacote. Você pode seguir este link para ver como ajustar a taxa de aprendizado de uma rede neural usando o PyTorch. Vamos definir os parâmetros, a perda e o otimizador:

num_epochs = 500                                        
# Traning the model for large number of epochs to see better results  
learning_rate = 0.0001                               
criterion = nn.BCELoss()                                
# We are working on lgistic regression so using Binary Cross Entropy
optimizer = torch.optim.SGD(lr.parameters(), lr=learning_rate)      
# Using ADAM optimizer to find local minima   

Acompanhamento do processo de treinamento:

for epoch in range(num_epochs):
    y_pred = lr(X_train)
    loss = criterion(y_pred, y_train)             
    loss.backward()
    optimizer.step()
    optimizer.zero_grad()
    if (epoch+1) % 20 == 0:                                         
        # printing loss values on every 10 epochs to keep track
        print(f'epoch: {epoch+1}, loss = {loss.item():.4f}')
epoch: 20, loss = 0.8447
epoch: 40, loss = 0.8379
epoch: 60, loss = 0.8316
epoch: 80, loss = 0.8257
epoch: 100, loss = 0.8203
epoch: 120, loss = 0.8152
epoch: 140, loss = 0.8106
epoch: 160, loss = 0.8063
epoch: 180, loss = 0.8023
epoch: 200, loss = 0.7986
epoch: 220, loss = 0.7952
epoch: 240, loss = 0.7920
epoch: 260, loss = 0.7891
epoch: 280, loss = 0.7863
epoch: 300, loss = 0.7838
epoch: 320, loss = 0.7815
epoch: 340, loss = 0.7793
epoch: 360, loss = 0.7773
epoch: 380, loss = 0.7755
epoch: 400, loss = 0.7737
epoch: 420, loss = 0.7721
epoch: 440, loss = 0.7706
epoch: 460, loss = 0.7692
epoch: 480, loss = 0.7679
epoch: 500, loss = 0.7667

Aqui, o primeiro passe para frente acontece. Em seguida, a perda é calculada. Quando loss.backward()é chamado, ele calcula o gradiente de perda em relação aos pesos (da camada). Os pesos são então atualizados chamando optimizer.step(). Depois disso, os pesos devem ser esvaziados para a próxima iteração. Então o zero_grad()método é chamado.

O código acima imprime a perda a cada 20ª época.

Desempenho do modelo

Vamos finalmente ver a precisão do modelo:

with torch.no_grad():
    y_predicted = lr(X_test)
    y_predicted_cls = y_predicted.round()
    acc = y_predicted_cls.eq(y_test).sum() / float(y_test.shape[0])
    print(f'accuracy: {acc.item():.4f}')
accuracy: 0.5093

Temos que usar torch.no_grad()aqui. O objetivo é omitir o cálculo do gradiente sobre os pesos. Portanto, qualquer coisa que eu colocar dentro desse loop não modificará os pesos e, portanto, não interromperá o processo de retropropagação.

Também podemos ver a precisão, o recall e a pontuação F1 usando o relatório de classificação:

#classification report
from sklearn.metrics import classification_report
print(classification_report(y_test, y_predicted_cls))
              precision    recall  f1-score   support

         0.0       0.51      0.62      0.56       270
         1.0       0.51      0.40      0.45       270

    accuracy                           0.51       540
   macro avg       0.51      0.51      0.50       540
weighted avg       0.51      0.51      0.50       540

Visualizando a Matriz de Confusão:

#confusion matrix
from sklearn.metrics import confusion_matrix
confusion_matrix = confusion_matrix(y_test, y_predicted_cls)
print(confusion_matrix)
[[168 102]
 [163 107]]

Conclusão

Observe que nosso modelo não funciona bem nesse conjunto de dados relativamente complexo. O objetivo deste tutorial é mostrar como fazer regressão logística no PyTorch. Se você deseja obter melhor precisão e outras métricas, considere ajustar os hiperparâmetros de treinamento, como aumentar o número de épocas e a taxa de aprendizado ou até mesmo adicionar mais uma camada (ou seja, rede neural).

Obtenha o código completo neste notebook Colab .

Fonte do artigo original em https://www.thepythoncode.com

Kennith  Blick

Kennith Blick

1625768100

Reading and Writing to Files in Python - Intermediate Python Tutorial #2

In this Python tutorial, we will learn how to deal with text files in Python using the built-in open function. You will understand how to use the most important modes: read, write and append.
That’s not all! We will discuss about file parsing and touch important string methods used for that such as: strip( ) and split( ). Finally we wrap up with a parsing file exercise to practice the new concepts. After that video you will be confident to deal with text files which is a very important skill to have as a programmer.

Playlist: Intermediate Python Tutorials | Video #2
Access the codes here: https://github.com/rscorrea1/youtube.git

Timestamp:
00:00 - Summary of the video
00:17 - Types of files
00:43 - How to open a file
01:17 - File modes
02:15 - How to read data from a file
03:00 - with statement
04:10 - readlines( ) method
05:05 - String: strip( ) method
06:22 - How to iterate over a file line by lin
08:47 - How to write data to a file
11:43 - How to append data to a file
12:37 - Exercise: Parsing a text file
16:14 - Converting data types
17:00 - Next video announcement

Thumbnail:
Photo by Mario Ho on Unsplash

#reading #writing #python #intermediate python tutorial #reading and writing to files in python

Reading and Writing to Files in Python - Intermediate Python Tutorial #2

Data Science Reading List — November

Interpretability vs. accuracy tradeoff
A hot topic in Data Science these days is the need for interpretability in models: the idea that, in some cases, we should give up some performance in order to be able to understand and explain exactly what the model is doing. One of the obstacles with this approach is the difficulty to define or measure interpretability. That is why, in this article, the authors propose to model the act of enforcing interpretability instead, so we can measure its statistical impacts.

#machine-learning #reading #data-science #computer-science #data

Data Science Reading List — November
Kacey  Hudson

Kacey Hudson

1617588761

Artificial Intelligence Recommended Reading List

Artificial Intelligence, Programming Languages, Quantum Computing, and more to come…
As I begin writing this, I have exactly 100 books and papers in my “reading now” folder, 18 books and papers in my “have read” folder, and 2 books in my “to read” folder. In other words, I have 100 titles that are in-progress to varying degrees, mostly because they are books and long papers, 18 titles that I have completely finished and not deleted afterward, and 2 more books that I haven’t started yet. I am constantly adding to my “to review” folder, which is where I keep the titles that I will either delete outright or move to my “to read” folder.
Among my “have read” titles, and even among my unfinished “reading now” titles, I have a select few titles marked as “favorites.” These are the titles that I have learned the most from and/or expect to refer back to in the future. While there are some non-favorite titles that I retain after completion, because you never know what the future holds, I will only be sharing my “favorites” in this article.
Because I try to read at least a little bit each day, I will be adding to this article as I mark more books and papers as my “favorites.”

#computer-science #ai #artificial-intelligence #quantum-computing #reading

Artificial Intelligence Recommended Reading List

Ysia Tamas

1603091469

Python script that reads articles on your behalf

Medium is one of the largest platforms for both readers and writers, it has a community of more than 300 million people around the world. Almost every day more than 100k articles are published on medium and more than 1000 hours of reading time every day. In medium, there are so many articles from over 100 different categories.

There is so much data to read on the medium that we can’t read by ourself but to increase our knowledge we have to more articles. so what do we do?

Here in this blog, I will share a way using which we can make our computer or laptop to read articles on their own so that you don’t have to read every article on your own. We are going to use Python and some of its libraries to create an article reader for us.

#reading #python #programming #productivity #coding

Python script that reads articles on your behalf
Mya  Lynch

Mya Lynch

1599116280

Weekly Machine Learning Research Paper Reading List — #5

LeSiNN: Detecting anomalies by identifying Least Similar Nearest Neighbours

Authors: Guansong Pang, Kai Ming Ting, and David Albrecht

Venue: 2015 IEEE International Conference on Data Mining Workshop (ICDMW)

Paper: URL

Abstract:

We introduce the concept of Least Similar Nearest Neighbours (LeSiNN) and use LeSiNN to detect anomalies directly. Although there is an existing method which is a special case of LeSiNN, this paper is the first to clearly articulate the underlying concept, as far as we know. LeSiNN is the first ensemble method which works well with models trained using samples of one instance. LeSiNN has linear time complexity with respect to data size and the number of dimensions, and it is one of the few anomaly detectors which can apply directly to both numeric and categorical data sets. Our extensive empirical evaluation shows that LeSiNN is either competitive to or better than six state-of-the-art anomaly detectors in terms of detection accuracy and runtime.


Explaining Outliers by Subspace Separability

Authors: Barbora Micenková, Raymond T. Ng, Xuan-Hong Dang, and Ira Assent

Venue: 2013 IEEE 13th International Conference on Data Mining (ICDM)

Paper: URL

Abstract:

Outliers are extraordinary objects in a data collection. Depending on the domain, they may represent errors, fraudulent activities or rare events that are subject of our interest. Existing approaches focus on detection of outliers or degrees of outlierness (ranking), but do not provide a possible explanation of how these objects deviate from the rest of the data.


#machine-learning #reading #academia #learning #research

Weekly Machine Learning Research Paper Reading List — #5