sponsored links

影象識別 | 基於Keras的手寫數字識別(含程式碼)

前沿

人工智慧的浪潮已經席捲全球,深度學習(Deep Learning)和人工智慧(Artificial Intelligence, AI)等詞彙也不斷地充斥在我們身邊。人工智慧的發展是一個三起兩落的變化,90年代期間,知識推理>神經網路>機器學習;2005年左右,機器學習>知識(語義網)>神經網路;而從2017年之後,基於深度學習的神經網路>知識(知識圖譜)>機器學習。

卷積神經網路(convolutional neural network, CNN)作為深度學習中的代表,最早的靈感是來源於1961年Hubel和Wiesel兩位神經生物學家,在對貓視覺皮層細胞的實驗中,發現大腦可視皮層是分層的(CNN中的分層網路結構與其如出一轍)。深度學習作為機器學習(ML)的一個子領域,由於計算機能力的提高和大量資料的可用性,得到了戲劇性的復甦。但是,深度學習是否能等同或代表人工智慧,這一點筆者認為有待商榷,深度學習可以認為是目前人工智慧發展階段的重要技術。由於本文主要撰寫關於深度學習的入門實戰,關於細節概念不做深入研究,下面筆者從實際案例,介紹深度學習處理影象的大致流程。

目錄:

以手寫識別數字為例,作為深度學習的入門專案,本文以Keras深度學習庫為基礎。其中使用的tensorflow等模組需要提前配置好,同時注意模型,圖片儲存、載入的檔案路徑問題。在自己的計算機上執行時,需要建立或修改。下面的流程包括:使用Keras載入MNIST資料集,構建Lenet訓練網路模型,使用Keras進行模型的儲存、載入,使用Keras實現對手寫數字資料集的訓練和預測,最後畫出誤差迭代圖。

手寫數字資料集介紹:

手寫數字識別幾乎是深度學習的入門資料集了。在keras中內建了MNIST資料集,其中測試集包含60000條資料,驗證集包含10000條資料,為單通道的灰度圖片,每張圖片的畫素大小為28×28.一共包含10個類別,為數字0到9。

匯入相關模組:

# import the necessary packages
import numpy as np
from keras.utils import np_utils
from keras.optimizers import Adam
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.layers.core import Activation
from keras.layers.core import Flatten
from keras.layers.core import Dense
from keras import backend as K
from keras.models import load_model  

載入MNIST資料集

Keras可實現多種神經網路模型,並可以載入多種資料集來評價模型的效果,下面我們使用程式碼自動載入MNIST資料集。

  # load minit data
from keras.datasets import mnist
(x_train, y_train),(x_test, y_test) = mnist.load_data()

顯示MNIST訓練資料集中的前面6張圖片:

  # plot 6 images as gray scale
import matplotlib.pyplot as plt

plt.subplot(321)
plt.imshow(x_train[0],cmap=plt.get_cmap('gray'))
plt.subplot(322)
plt.imshow(x_train[1],cmap=plt.get_cmap('gray'))
plt.subplot(323)
plt.imshow(x_train[2],cmap=plt.get_cmap('gray'))
plt.subplot(324)
plt.imshow(x_train[3],cmap=plt.get_cmap('gray'))
plt.subplot(325)
plt.imshow(x_train[4],cmap=plt.get_cmap('gray'))
plt.subplot(326)
plt.imshow(x_train[5],cmap=plt.get_cmap('gray'))
# show
plt.show()

資料的預處理

首先,將資料轉換為4維向量[samples][width][height][pixels],以便於後面模型的輸入

  # reshape the data to four dimensions, due to the input of model
# reshape to be [samples][width][height][pixels]
x_train = x_train.reshape(x_train.shape[0], 28, 28, 1).astype('float32')
x_test = x_test.reshape(x_test.shape[0], 28, 28, 1).astype('float32')

為了使模型訓練效果更好,通常需要對影象進行歸一化處理

  # normalization
x_train = x_train / 255.0
x_test = x_test / 255.0

最後,原始MNIST資料集的資料標籤是0-9,通常要將其表示成one-hot向量。如訓練資料標籤為1,則將其轉化為向量[0,1,0,0,0,0,0,0,0,0]

  # one-hot
y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)

模型的建立與計算

訓練模型的引數設定:

  # parameters
EPOCHS = 10
INIT_LR = 1e-3
BS = 32
CLASS_NUM = 10
norm_size = 28

本文使用Lenet網路架構,下面定義Lenet網路結構,若要更改網路結構,如用VGGNet,GoogleNet,Inception,ResNets或自己構建不同的網路結構,可以直接在這一塊函式內進行修改。

  # define lenet model
def l_model(width, height, depth, NB_CLASS):
    model = Sequential()
    inputShape = (height, width, depth)
    # if we are using "channels last", update the input shape
    if K.image_data_format() == "channels_first":  # for tensorflow
        inputShape = (depth, height, width)
    # first set of CONV => RELU => POOL layers
    model.add(Conv2D(20, (5, 5), padding="same", input_shape=inputShape))
    model.add(Activation("relu"))
    model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
    # second set of CONV => RELU => POOL layers
    model.add(Conv2D(50, (5, 5), padding="same"))
    model.add(Activation("relu"))
    model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
    # first (and only) set of FC => RELU layers
    model.add(Flatten())
    model.add(Dense(500))
    model.add(Activation("relu"))

    # softmax classifier
    model.add(Dense(NB_CLASS))
    model.add(Activation("softmax"))

    # return the constructed network architecture
    return model

再附上兩個經典的模型:

VGG16:

import inspect
import os

import numpy as np
import tensorflow as tf
import time

VGG_MEAN = [103.939, 116.779, 123.68]

class Vgg16:
    def __init__(self, vgg16_npy_path=None):
        if vgg16_npy_path is None:
            path = inspect.getfile(Vgg16)
            path = os.path.abspath(os.path.join(path, os.pardir))
            path = os.path.join(path, "vgg16.npy")
            vgg16_npy_path = path
            print(path)

        self.data_dict = np.load(vgg16_npy_path, encoding='latin1').item()
        print("npy file loaded")

    def build(self, rgb):
        """
        load variable from npy to build the VGG
        :param rgb: rgb image [batch, height, width, 3] values scaled [0, 1]
        """

        start_time = time.time()
        print("build model started")
        rgb_scaled = rgb * 255.0

        # Convert RGB to BGR
        red, green, blue = tf.split(axis=3, num_or_size_splits=3, value=rgb_scaled)
        assert red.get_shape().as_list()[1:] == [224, 224, 1]
        assert green.get_shape().as_list()[1:] == [224, 224, 1]
        assert blue.get_shape().as_list()[1:] == [224, 224, 1]
        bgr = tf.concat(axis=3, values=[
            blue - VGG_MEAN[0],
            green - VGG_MEAN[1],
            red - VGG_MEAN[2],
        ])
        assert bgr.get_shape().as_list()[1:] == [224, 224, 3]

        self.conv1_1 = self.conv_layer(bgr, "conv1_1")
        self.conv1_2 = self.conv_layer(self.conv1_1, "conv1_2")
        self.pool1 = self.max_pool(self.conv1_2, 'pool1')

        self.conv2_1 = self.conv_layer(self.pool1, "conv2_1")
        self.conv2_2 = self.conv_layer(self.conv2_1, "conv2_2")
        self.pool2 = self.max_pool(self.conv2_2, 'pool2')

        self.conv3_1 = self.conv_layer(self.pool2, "conv3_1")
        self.conv3_2 = self.conv_layer(self.conv3_1, "conv3_2")
        self.conv3_3 = self.conv_layer(self.conv3_2, "conv3_3")
        self.pool3 = self.max_pool(self.conv3_3, 'pool3')

        self.conv4_1 = self.conv_layer(self.pool3, "conv4_1")
        self.conv4_2 = self.conv_layer(self.conv4_1, "conv4_2")
        self.conv4_3 = self.conv_layer(self.conv4_2, "conv4_3")
        self.pool4 = self.max_pool(self.conv4_3, 'pool4')

        self.conv5_1 = self.conv_layer(self.pool4, "conv5_1")
        self.conv5_2 = self.conv_layer(self.conv5_1, "conv5_2")
        self.conv5_3 = self.conv_layer(self.conv5_2, "conv5_3")
        self.pool5 = self.max_pool(self.conv5_3, 'pool5')

        self.fc6 = self.fc_layer(self.pool5, "fc6")
        assert self.fc6.get_shape().as_list()[1:] == [4096]
        self.relu6 = tf.nn.relu(self.fc6)

        self.fc7 = self.fc_layer(self.relu6, "fc7")
        self.relu7 = tf.nn.relu(self.fc7)

        self.fc8 = self.fc_layer(self.relu7, "fc8")

        self.prob = tf.nn.softmax(self.fc8, name="prob")

        self.data_dict = None
        print(("build model finished: %ds" % (time.time() - start_time)))

    def avg_pool(self, bottom, name):
        return tf.nn.avg_pool(bottom, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME', name=name)

    def max_pool(self, bottom, name):
        return tf.nn.max_pool(bottom, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME', name=name)

    def conv_layer(self, bottom, name):
        with tf.variable_scope(name):
            filt = self.get_conv_filter(name)

            conv = tf.nn.conv2d(bottom, filt, [1, 1, 1, 1], padding='SAME')

            conv_biases = self.get_bias(name)
            bias = tf.nn.bias_add(conv, conv_biases)

            relu = tf.nn.relu(bias)
            return relu

    def fc_layer(self, bottom, name):
        with tf.variable_scope(name):
            shape = bottom.get_shape().as_list()
            dim = 1
            for d in shape[1:]:
                dim *= d
            x = tf.reshape(bottom, [-1, dim])

            weights = self.get_fc_weight(name)
            biases = self.get_bias(name)

            # Fully connected layer. Note that the '+' operation automatically
            # broadcasts the biases.
            fc = tf.nn.bias_add(tf.matmul(x, weights), biases)

            return fc

    def get_conv_filter(self, name):
        return tf.constant(self.data_dict[name][0], name="filter")

    def get_bias(self, name):
        return tf.constant(self.data_dict[name][1], name="biases")

    def get_fc_weight(self, name):
        return tf.constant(self.data_dict[name][0], name="weights")

GoogleNet:

from keras.models import Model
from keras.utils import plot_model
from keras import regularizers
from keras import backend as K
from keras.layers import Input,Flatten, Dense,Dropout,BatchNormalization, concatenate
from keras.layers.convolutional import Conv2D,MaxPooling2D,AveragePooling2D

# Global Constants
NB_CLASS=20
LEARNING_RATE=0.01
MOMENTUM=0.9
ALPHA=0.0001
BETA=0.75
GAMMA=0.1
DROPOUT=0.4
WEIGHT_DECAY=0.0005
LRN2D_NORM=True
DATA_FORMAT='channels_last' # Theano:'channels_first' Tensorflow:'channels_last'
USE_BN=True
IM_WIDTH=224
IM_HEIGHT=224
EPOCH=50

def conv2D_lrn2d(x,filters,kernel_size,strides=(1,1),padding='same',dilation_rate=(1,1),activation='relu',
                 use_bias=True,kernel_initializer='glorot_uniform',bias_initializer='zeros',
                 kernel_regularizer=None,bias_regularizer=None,activity_regularizer=None,
                 kernel_constraint=None,bias_constraint=None,lrn2d_norm=LRN2D_NORM,weight_decay=WEIGHT_DECAY):
    #l2 normalization
    if weight_decay:
        kernel_regularizer=regularizers.l2(weight_decay)
        bias_regularizer=regularizers.l2(weight_decay)
    else:
        kernel_regularizer=None
        bias_regularizer=None
    x=Conv2D(filters=filters,kernel_size=kernel_size,strides=strides,padding=padding,dilation_rate=dilation_rate,
             activation=activation,use_bias=use_bias,kernel_initializer=kernel_initializer,
             bias_initializer=bias_initializer,kernel_regularizer=kernel_regularizer,bias_regularizer=bias_regularizer,
             activity_regularizer=activity_regularizer,kernel_constraint=kernel_constraint,bias_constraint=bias_constraint)(x)
    if lrn2d_norm:
        #batch normalization
        x=BatchNormalization()(x)

    return x

def inception_module(x,params,concat_axis,padding='same',dilation_rate=(1,1),activation='relu',
                     use_bias=True,kernel_initializer='glorot_uniform',bias_initializer='zeros',
                     kernel_regularizer=None,bias_regularizer=None,activity_regularizer=None,kernel_constraint=None,
                     bias_constraint=None,lrn2d_norm=LRN2D_NORM,weight_decay=None):
    (branch1,branch2,branch3,branch4)=params
    if weight_decay:
        kernel_regularizer=regularizers.l2(weight_decay)
        bias_regularizer=regularizers.l2(weight_decay)
    else:
        kernel_regularizer=None
        bias_regularizer=None
    #1x1
    pathway1=Conv2D(filters=branch1[0],kernel_size=(1,1),strides=1,padding=padding,dilation_rate=dilation_rate,
                    activation=activation,use_bias=use_bias,kernel_initializer=kernel_initializer,
                    bias_initializer=bias_initializer,kernel_regularizer=kernel_regularizer,bias_regularizer=bias_regularizer,
                    activity_regularizer=activity_regularizer,kernel_constraint=kernel_constraint,bias_constraint=bias_constraint)(x)
    #1x1->3x3
    pathway2=Conv2D(filters=branch2[0],kernel_size=(1,1),strides=1,padding=padding,dilation_rate=dilation_rate,
                    activation=activation,use_bias=use_bias,kernel_initializer=kernel_initializer,
                    bias_initializer=bias_initializer,kernel_regularizer=kernel_regularizer,bias_regularizer=bias_regularizer,
                    activity_regularizer=activity_regularizer,kernel_constraint=kernel_constraint,bias_constraint=bias_constraint)(x)
    pathway2=Conv2D(filters=branch2[1],kernel_size=(3,3),strides=1,padding=padding,dilation_rate=dilation_rate,
                    activation=activation,use_bias=use_bias,kernel_initializer=kernel_initializer,
                    bias_initializer=bias_initializer,kernel_regularizer=kernel_regularizer,bias_regularizer=bias_regularizer,
                    activity_regularizer=activity_regularizer,kernel_constraint=kernel_constraint,bias_constraint=bias_constraint)(pathway2)
    #1x1->5x5
    pathway3=Conv2D(filters=branch3[0],kernel_size=(1,1),strides=1,padding=padding,dilation_rate=dilation_rate,
                    activation=activation,use_bias=use_bias,kernel_initializer=kernel_initializer,
                    bias_initializer=bias_initializer,kernel_regularizer=kernel_regularizer,bias_regularizer=bias_regularizer,
                    activity_regularizer=activity_regularizer,kernel_constraint=kernel_constraint,bias_constraint=bias_constraint)(x)
    pathway3=Conv2D(filters=branch3[1],kernel_size=(5,5),strides=1,padding=padding,dilation_rate=dilation_rate,
                    activation=activation,use_bias=use_bias,kernel_initializer=kernel_initializer,
                    bias_initializer=bias_initializer,kernel_regularizer=kernel_regularizer,bias_regularizer=bias_regularizer,
                    activity_regularizer=activity_regularizer,kernel_constraint=kernel_constraint,bias_constraint=bias_constraint)(pathway3)
    #3x3->1x1
    pathway4=MaxPooling2D(pool_size=(3,3),strides=1,padding=padding,data_format=DATA_FORMAT)(x)
    pathway4=Conv2D(filters=branch4[0],kernel_size=(1,1),strides=1,padding=padding,dilation_rate=dilation_rate,
                    activation=activation,use_bias=use_bias,kernel_initializer=kernel_initializer,
                    bias_initializer=bias_initializer,kernel_regularizer=kernel_regularizer,bias_regularizer=bias_regularizer,
                    activity_regularizer=activity_regularizer,kernel_constraint=kernel_constraint,bias_constraint=bias_constraint)(pathway4)

    return concatenate([pathway1,pathway2,pathway3,pathway4],axis=concat_axis)

class GoogleNet:
    @staticmethod
    def build(width, height, depth, NB_CLASS):
        INP_SHAPE = (height, width, depth)
        img_input = Input(shape=INP_SHAPE)
        CONCAT_AXIS = 3
        # Data format:tensorflow,channels_last;theano,channels_last
        if K.image_data_format() == 'channels_first':
            INP_SHAPE = (depth, height, width)
            img_input = Input(shape=INP_SHAPE)
            CONCAT_AXIS = 1
        x = conv2D_lrn2d(img_input, 64, (7, 7), 2, padding='same', lrn2d_norm=False)
        x = MaxPooling2D(pool_size=(2, 2), strides=2, padding='same')(x)
        x = BatchNormalization()(x)

        x = conv2D_lrn2d(x, 64, (1, 1), 1, padding='same', lrn2d_norm=False)

        x = conv2D_lrn2d(x, 192, (3, 3), 1, padding='same', lrn2d_norm=True)
        x = MaxPooling2D(pool_size=(2, 2), strides=2, padding='same')(x)

        x = inception_module(x, params=[(64,), (96, 128), (16, 32), (32,)], concat_axis=CONCAT_AXIS)  # 3a
        x = inception_module(x, params=[(128,), (128, 192), (32, 96), (64,)], concat_axis=CONCAT_AXIS)  # 3b
        x = MaxPooling2D(pool_size=(2, 2), strides=2, padding='same')(x)

        x = inception_module(x, params=[(192,), (96, 208), (16, 48), (64,)], concat_axis=CONCAT_AXIS)  # 4a
        x = inception_module(x, params=[(160,), (112, 224), (24, 64), (64,)], concat_axis=CONCAT_AXIS)  # 4b
        x = inception_module(x, params=[(128,), (128, 256), (24, 64), (64,)], concat_axis=CONCAT_AXIS)  # 4c
        x = inception_module(x, params=[(112,), (144, 288), (32, 64), (64,)], concat_axis=CONCAT_AXIS)  # 4d
        x = inception_module(x, params=[(256,), (160, 320), (32, 128), (128,)], concat_axis=CONCAT_AXIS)  # 4e
        x = MaxPooling2D(pool_size=(2, 2), strides=2, padding='same')(x)

        x = inception_module(x, params=[(256,), (160, 320), (32, 128), (128,)], concat_axis=CONCAT_AXIS)  # 5a
        x = inception_module(x, params=[(384,), (192, 384), (48, 128), (128,)], concat_axis=CONCAT_AXIS)  # 5b
        x = AveragePooling2D(pool_size=(1, 1), strides=1, padding='valid')(x)

        x = Flatten()(x)
        x = Dropout(DROPOUT)(x)
        x = Dense(output_dim=NB_CLASS, activation='linear')(x)
        x = Dense(output_dim=NB_CLASS, activation='softmax')(x)

        # Create a Keras Model
        model = Model(input=img_input, output=[x])
        model.summary()
        # Save a PNG of the Model Build
        #plot_model(model, to_file='../imgs/GoogLeNet.png')
        # return the constructed network architecture
        return model

設定最佳化方法,loss函式,並編譯模型:

model = l_model(width=norm_size, height=norm_size, depth=1, NB_CLASS=CLASS_NUM)
opt = Adam(lr=INIT_LR, decay=INIT_LR / EPOCHS)
model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=["accuracy"])

本文使用生成器以節約記憶體:

# Use generators to save memory
aug = ImageDataGenerator(rotation_range=30, width_shift_range=0.1,
                             height_shift_range=0.1, shear_range=0.2, zoom_range=0.2,
                             horizontal_flip=True, fill_mode="nearest")

H = model.fit_generator(aug.flow(x_train, y_train, batch_size=BS),
                            steps_per_epoch=len(x_train) // BS,
                            epochs=EPOCHS, verbose=2)

結果分析

作出訓練階段的損失、精確度迭代圖,本文將epoch設定為10,已達到0.98的準確率(程式碼、影象如下所示)。

# plot the iteration process
N = EPOCHS
plt.figure()
plt.plot(np.arange(0,N),H.history['loss'],label='loss')
plt.plot(np.arange(0,N),H.history['acc'],label='train_acc')
plt.title('Training Loss and Accuracy on mnist-img classifier')
plt.xlabel('Epoch')
plt.ylabel('Loss/Accuracy')
plt.legend(loc='lower left')
plt.savefig('../figure/Figure_2.png')

公眾號:帕帕科技喵

歡迎關注與討論~

分類: 娛樂
時間: 2022-01-19

相關文章

深度解析:讓美軍深感威脅的解放軍3大變化?核武器並非最重要

深度解析:讓美軍深感威脅的解放軍3大變化?核武器並非最重要
對於美軍來說,現在的世界不像以前那樣"唾手可得"了.在中國人民解放軍成長為全球第二大軍事力量以前,他們只需要擔心一下蘇聯解體後,手握蘇聯遺產的俄羅斯軍隊.至少在那個時候,美國的霸權 ...

越軍活捉張萬年,43軍軍長急電127師,得知位置明白“活捉”原委

越軍活捉張萬年,43軍軍長急電127師,得知位置明白“活捉”原委
1979年3月4日清晨6時,越南第一軍區向世界宣稱,他們的精銳特工消滅了中國"鐵軍師"的師指揮部,活捉了中國戰神"張萬年".一時間,獲悉此訊息的越南人可謂是炸開 ...

抗美援朝戰場上,不是你想象的那樣,其實美軍經常伏擊志願軍

抗美援朝戰場上,不是你想象的那樣,其實美軍經常伏擊志願軍
1950年6月25號凌晨4時,朝鮮內閣首相金日成在徵得斯大林的同意之後,下令軍隊越過三八線,由此朝鮮戰爭爆發.作為二戰結束初期的一場大規模區域性戰爭,朝鮮戰爭雖僅在朝鮮半島範圍內展開,卻牽扯了多個國家 ...

男兵和傷員全被害,7名女衛生兵被俘?對越自衛還擊戰典型謠言
說起對越自衛反擊戰(涵蓋十年中越戰爭),雖然已經是30多年前的往事,但卻是我軍參加的最後一場戰爭,體現了我軍當時的軍事水平,所以坊間對此有著持久而濃厚的興趣.不過出於眾所周知的原因,這場戰爭的具體過程 ...

特務土匪陳靖熊:遠逃千里當農民,漂亮老婆三次假嫁潛伏十年

特務土匪陳靖熊:遠逃千里當農民,漂亮老婆三次假嫁潛伏十年
瀘溪巨匪陳靖熊,不僅是土匪中的一個"另類",而且也是特務中的一個"奇才"!他早年參加共產黨,少年得志,官至工人糾察隊總隊長.被俘後背叛革命,出賣靈魂,又混為國軍 ...

慘烈的法卡山戰役:邊防軍多個班排全員覆沒,倖存者戰後相擁而泣

慘烈的法卡山戰役:邊防軍多個班排全員覆沒,倖存者戰後相擁而泣
20世紀70年代,東南亞局勢被越南攪得天翻地覆.北越在1975年統一了南越,隨後在蘇聯的支援下控制了寮國,又出兵滅掉了鄰國柬埔寨,還向北蠶食中越邊境. 忍無可忍的中國在1979年2月發起了" ...

肖家喜在越南戰場上中彈,獨自爬行9天8夜回到祖國,如今怎麼樣了

肖家喜在越南戰場上中彈,獨自爬行9天8夜回到祖國,如今怎麼樣了
在戰場上,為掩護隊友,他身負重傷孤膽突圍越軍防線,連續爬行8個晝夜艱難回國,未殺一敵被授予"一等功臣",時任國家副主席.開國上將親自前去探望他,他究竟是誰?在他的身上究竟發生了什麼 ...

沙家店戰役,毛主席離戰場只20裡,興奮地說:我看胡宗南怎麼交待

沙家店戰役,毛主席離戰場只20裡,興奮地說:我看胡宗南怎麼交待
作者:太史小生 歷史的車輪滾滾向前.國民黨軍在對解放區的進攻中一次次受挫,終成強弩之末. 毛主席和他的戰友們在西柏坡村那間不起眼的農舍裡,組成了世界上最小的司令部,開始謀劃對國民黨的全線戰略進攻. 一 ...

瞄準打贏,向著未來戰場衝鋒不止
劉近(左)在為戰士講解疊傘要領. 張永進攝 賀蘭山下,夕陽灑滿大地,一支鬥志昂揚的隊伍結束了一天的訓練.雖然全身被泥土和汗水包裹,但官兵們的口號聲依舊嘹亮,響徹晴空. 帶隊的營長叫劉近,來自陸軍第七十 ...

廣西永福縣清剿土匪紀事

廣西永福縣清剿土匪紀事
廣西永福.百壽兩縣剿匪鬥爭於1949年12月下旬開始,至1951年5月結束,經過1年半的艱苦 奮戰,根除了匪患. 解放初,流竄.潛伏百壽和永福的股匪有:"東北交警部隊第一旅",又稱 ...

1988年,我軍截獲越軍電報:解放軍有一神槍手,31發子彈殺敵30人

1988年,我軍截獲越軍電報:解放軍有一神槍手,31發子彈殺敵30人
1988年10月,我國南部邊防的一個通訊兵意外截獲了越軍發出的一條電報: "一個多月來,本部已有30名官兵在戰前偵查中陣亡,1名重傷,經核實,31發子彈都是從他們的頭部.胸部等要害位置穿過, ...

柯萊之戰:越軍305師三次夜襲,被解放軍詭雷陣重創,抬擔架運走

柯萊之戰:越軍305師三次夜襲,被解放軍詭雷陣重創,抬擔架運走
1979年3月8日,號稱"特別精銳部隊"的越南特工若不是遇到解放軍第128師步兵第384團8連的戰士,或許,他們會認為,搞特種作戰,他們說第二,沒人敢說第一.然而,老天爺跟他們開了 ...

對越十年作戰,解放軍炮兵創紀錄的一戰,越軍也承認了

對越十年作戰,解放軍炮兵創紀錄的一戰,越軍也承認了
1984年4月28日至5月1日,中國人民解放軍昆明軍區前指指揮陸軍第14軍和陸軍第31師,分別收復了被越軍侵佔5年之久的雲南省麻栗坡縣老山地區和者陰山地區.經過4天的進攻作戰,基本殲滅了越軍313師1 ...

越軍老兵回憶老山戰場,害怕和解放軍打仗,逃兵現象嚴重

越軍老兵回憶老山戰場,害怕和解放軍打仗,逃兵現象嚴重
312師是越軍組建的第三個機動主力師,別稱"戰勝師",成立於1950年11月初,下轄步兵第141團.165團.209團和炮兵第186團.該師組建時間早,參加實戰多(參加過抗法戰爭時 ...

解放軍記者探訪戰俘營,對越軍俘虜有兩個深刻印象
1979年的對越自衛還擊.保衛邊疆作戰期間,為了做好越軍俘虜的收容.管理.教育工作,我軍組建了俘虜收容所.收容所為團級單位,有主任.政委各一名,副主任兩名,協理員一名,設管理股.文藝股.登記股.後勤股 ...

越南記錄的對越反擊戰:越軍一個連阻擊瞭解放軍一個師

越南記錄的對越反擊戰:越軍一個連阻擊瞭解放軍一個師
316師是越軍早期組建的第五個主力步兵師,於1951年5月1日在諒山省谷龍地區正式成立,當時下轄步兵第98團.174團.176團及兵種分隊,全師共7000餘人,首任師長黎廣波.政委朱輝珉.該師別稱&q ...

高平戰役:越軍主力消失,解放軍傷亡不斷,許世友:地毯式清剿

高平戰役:越軍主力消失,解放軍傷亡不斷,許世友:地毯式清剿
高平戰役是對越自衛反擊戰中規模最大.最激烈的一次戰役,總共打了28天,貫穿整個反擊戰,我軍先後投入11個師,總兵力超過十萬人,最終殲敵將近2萬. 高平是越北重鎮,距離中越邊境僅20多公里,有三條公路穿 ...

軋過去!解放軍坦克將越軍火炮頂得底朝天,留下著名戰場寫真

軋過去!解放軍坦克將越軍火炮頂得底朝天,留下著名戰場寫真
作者:朱炳東 1979年2月17日,41軍122師攻打越南朔江,採用明修棧道.暗渡陳倉的戰術,365團與軍坦克團3營8連從平孟沿公路向朔江進攻,吸引越軍注意力.而主力從孟麻秘密出境.配屬作戰的軍坦克團 ...

解放軍重返懷玉山,鄉親們奔走相告:方誌敏的弟弟打回來了

解放軍重返懷玉山,鄉親們奔走相告:方誌敏的弟弟打回來了
作者:卡迪羅 1948年底,我軍在遼瀋戰役中大獲全勝,淮海.平津戰役也勝利在望,解放戰爭的全國性勝利已成定局.在勝利形勢的鼓舞下,在長江以南的皖南地區活動的三支解放軍游擊隊皖贛支隊.皖浙贛支隊和黃西支 ...

庭毫山之戰:越軍被41軍包圍70天,士兵崩潰射殺軍官,政治員投降

庭毫山之戰:越軍被41軍包圍70天,士兵崩潰射殺軍官,政治員投降
庭毫山事件後續(接前文) 1978年11月上旬,越軍入侵我國廣西靖西邊境,製造了震驚世界的"庭毫山流血事件"後,不顧世界各國的譴責,反而在蘇聯的撐腰下,強行派兵駐紮在我國庭毫山地區 ...