【神经网络】(6) 卷积神经网络(VGG16),案例:鸟类图片4分类_立Sir的博客-程序员秘密

技术标签: cnn  tensorflow  python  深度学习  神经网络  TensorFlow神经网络  

各位同学好,今天和大家分享一下TensorFlow2.0中的VGG16卷积神经网络模型,案例:现在有四种鸟类的图片各200张,构建卷积神经网络,预测图片属于哪个分类。

1. 数据加载

将鸟类图片按类分开存放,使用tf.keras.preprocessing.image_dataset_from_directory()函数分批次读取图片数据,统一指定图片加载进来的大小224*224,指定参数label_model'int'代表目标值y是数值类型,即0, 1, 2, 3等;'categorical'代表onehot类型,对应索引的值为1,如图像属于第二类则表示为0,1,0,0,0;'binary'代表二分类

import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import Sequential, optimizers, layers, Model

#(1)数据加载
def get_data(height, width, batchsz):
    # 获取训练集数据
    filepath1 = 'C:/Users/admin/.spyder-py3/test/数据集/4种鸟分类/new_data/train'
    train_ds = tf.keras.preprocessing.image_dataset_from_directory(
        filepath1, # 指定训练集数据路径
        label_mode = 'categorical',  # 进行onehot编码
        image_size = (height, width), # 对图像risize
        batch_size = batchsz, # 每次迭代取32个数据 
        )
    
    # 获取验证集数据
    filepath2 = 'C:/Users/admin/.spyder-py3/test/数据集/4种鸟分类/new_data/val'
    val_ds = tf.keras.preprocessing.image_dataset_from_directory(
        filepath1, # 指定训练集数据路径
        label_mode = 'categorical',  
        image_size = (height, width), # 对图像risize
        batch_size = batchsz, # 每次迭代取32个数据 
        )    
    
    # 获取测试集数据
    filepath2 = 'C:/Users/admin/.spyder-py3/test/数据集/4种鸟分类/new_data/test'
    test_ds = tf.keras.preprocessing.image_dataset_from_directory(
        filepath1, # 指定训练集数据路径
        label_mode = 'categorical',  
        image_size = (height, width), # 对图像risize
        batch_size = batchsz, # 每次迭代取32个数据 
        )      
    
    # 返回数据集
    return train_ds, val_ds, test_ds


# 数据读取函数,返回训练集、验证集、测试集
train_ds, val_ds, test_ds = get_data(height=224, width=224, batchsz=32) 
# 查看有哪些分类
class_names = train_ds.class_names
print('类别有:', class_names)
# 类别有: ['Bananaquit', 'Black Skimmer', 'Black Throated Bushtiti', 'Cockatoo']


# 查看数据集信息
sample = next(iter(train_ds)) #每次取出一个batch的训练数据
print('x_batch.shape:', sample[0].shape, 'y_batch.shape:',sample[1].shape)
# x_batch.shape: (128, 224, 224, 3) y_batch.shape: (128, 4)
print('y[:5]:', sample[1][:5]) # 查看前五个目标值

2. 数据预处理

使用.map()函数转换数据集中所有x和y的类型,并将每张图象的像素值映射到[-1,1]之间,打乱训练集数据的顺序.shuffle(),但不改变特征值x和标签值y之间的对应关系。iter()生成迭代器,配合next()每次运行取出训练集中的一个batch数据

#(2)显示图像
import matplotlib.pyplot as plt
for i in range(15):
    plt.subplot(3,5,i+1)
    plt.imshow(sample[0][i]/255.0) # sample[0]代表取出的一个batch的所有图像信息,映射到[0,1]之间显示图像
    plt.xticks([]) # 不显示xy轴坐标刻度
    plt.yticks([])
plt.show()

#(3)数据预处理
# 定义预处理函数
def processing(x, y):
    x = 2 * tf.cast(x, dtype=tf.float32)/255.0 - 1  #映射到[-1,1]之间
    y = tf.cast(y, dtype=tf.int32) # 转换数据类型
    return x,y
    
# 对所有数据集预处理
train_ds = train_ds.map(processing).shuffle(10000)
val_ds = val_ds.map(processing)
test_ds = test_ds.map(processing)

# 再次查看数据信息
sample = next(iter(train_ds)) #每次取出一个batch的训练数据
print('x_batch.shape:', sample[0].shape, 'y_batch.shape:',sample[1].shape)
# x_batch.shape: (128, 224, 224, 3) y_batch.shape: (128, 4)
print('y[:5]:', sample[1][:5]) # 查看前五个目标值
# [[0 0 1 0], [1 0 0 0], [0 1 0 0], [0 1 0 0], [1 0 0 0]]

鸟类图像如下:


3. VGG16网络构造

VGG16的模型框架如下图所示,原理见下文:深度学习-VGG16原理详解 。

1)输入图像尺寸为224x224x3,经64个通道为3的3x3的卷积核,步长为1,padding=same填充,卷积两次,再经ReLU激活,输出的尺寸大小为224x224x64

2)经max pooling(最大化池化),滤波器为2x2,步长为2,图像尺寸减半,池化后的尺寸变为112x112x64

3)经128个3x3的卷积核,两次卷积,ReLU激活,尺寸变为112x112x128

4)max pooling池化,尺寸变为56x56x128

5)经256个3x3的卷积核,三次卷积,ReLU激活,尺寸变为56x56x256

6)max pooling池化,尺寸变为28x28x256

7)经512个3x3的卷积核,三次卷积,ReLU激活,尺寸变为28x28x512

8)max pooling池化,尺寸变为14x14x512

9)经512个3x3的卷积核,三次卷积,ReLU,尺寸变为14x14x512

10)max pooling池化,尺寸变为7x7x512

11)然后Flatten(),将数据拉平成向量,变成一维51277=25088。

11)再经过两层1x1x4096,一层1x1x1000的全连接层(共三层),经ReLU激活

12)最后通过softmax输出1000个预测结果
 

下面通过代码来实现,这里我们需要的是4分类,因此把最后的1000个预测结果改为4既可。

#(4)构建CNN-VGG16
def VGG16(input_shape=(224,224,3), output_shape=4):
    # 输入层
    input_tensor = keras.Input(shape=input_shape)
    
    # unit1
    # 卷积层
    x = layers.Conv2D(64, (3,3), activation='relu', strides=1, padding='same')(input_tensor) # [224,224,64]
    # 卷积层
    x = layers.Conv2D(64, (3,3), activation='relu' , strides=1, padding='same')(x) #[224,224,64]
    # 池化层,size变成1/2
    x = layers.MaxPool2D(pool_size=(2,2), strides=(2,2))(x) #[112,112,64]
    
    # unit2
    # 卷积层
    x = layers.Conv2D(128, (3,3), activation='relu', strides=1, padding='same')(x) #[112,112,128]
    # 卷积层
    x = layers.Conv2D(128, (3,3), activation='relu', strides=1, padding='same')(x) #[112,112,128]
    # 池化层
    x = layers.MaxPool2D(pool_size=(2,2), strides=(2,2))(x) #[56,56,128]

    # unit3
    # 卷积层
    x = layers.Conv2D(256, (3,3), activation='relu', strides=1, padding='same')(x) #[56,56,256]
    # 卷积层
    x = layers.Conv2D(256, (3,3), activation='relu', strides=1, padding='same')(x) #[56,56,256]
    # 卷积层
    x = layers.Conv2D(256, (3,3), activation='relu', strides=1, padding='same')(x) #[56,56,256]
    # 池化层
    x = layers.MaxPool2D(pool_size=(2,2), strides=(2,2))(x) #[28,28,256]
    
    # unit4
    # 卷积层
    x = layers.Conv2D(512, (3,3), activation='relu', strides=1, padding='same')(x) #[28,28,512]
    # 卷积层
    x = layers.Conv2D(512, (3,3), activation='relu', strides=1, padding='same')(x) #[28,28,512]    
    # 卷积层
    x = layers.Conv2D(512, (3,3), activation='relu', strides=1, padding='same')(x) #[28,28,512]
    # 池化层
    x = layers.MaxPool2D(pool_size=(2,2), strides=(2,2))(x) #[14,14,512]
    
    # unit5
    # 卷积层
    x = layers.Conv2D(512, (3,3), activation='relu', strides=1, padding='same')(x) #[14,14,512]
    # 卷积层
    x = layers.Conv2D(512, (3,3), activation='relu', strides=1, padding='same')(x) #[14,14,512]
    # 卷积层
    x = layers.Conv2D(512, (3,3), activation='relu', strides=1, padding='same')(x) #[14,14,512]
    # 池化层
    x = layers.MaxPool2D(pool_size=(2,2), strides=(2,2))(x) #[7,7,512]
    
    # uint6
    # Flatten层
    x = layers.Flatten()(x) #压平[None,4096]
    # 全连接层
    x = layers.Dense(4096, activation='relu')(x) #[None,4096]
    # 全连接层
    x = layers.Dense(4096, activation='relu')(x) #[None,4096]
    # 输出层,输出结果不做softmax
    output_tensor = layers.Dense(output_shape)(x) #[None,4]
    
    # 构建模型
    model = Model(inputs=input_tensor, outputs=output_tensor)
    
    # 返回模型
    return model

# 构建VGG16模型
model = VGG16()
# 查看模型结构
model.summary()

该网络构架如下

Model: "model"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input_1 (InputLayer)        [(None, 224, 224, 3)]     0         
                                                                 
 conv2d (Conv2D)             (None, 224, 224, 64)      1792      
                                                                 
 conv2d_1 (Conv2D)           (None, 224, 224, 64)      36928     
                                                                 
 max_pooling2d (MaxPooling2D  (None, 112, 112, 64)     0         
 )                                                               
                                                                 
 conv2d_2 (Conv2D)           (None, 112, 112, 128)     73856     
                                                                 
 conv2d_3 (Conv2D)           (None, 112, 112, 128)     147584    
                                                                 
 max_pooling2d_1 (MaxPooling  (None, 56, 56, 128)      0         
 2D)                                                             
                                                                 
 conv2d_4 (Conv2D)           (None, 56, 56, 256)       295168    
                                                                 
 conv2d_5 (Conv2D)           (None, 56, 56, 256)       590080    
                                                                 
 conv2d_6 (Conv2D)           (None, 56, 56, 256)       590080    
                                                                 
 max_pooling2d_2 (MaxPooling  (None, 28, 28, 256)      0         
 2D)                                                             
                                                                 
 conv2d_7 (Conv2D)           (None, 28, 28, 512)       1180160   
                                                                 
 conv2d_8 (Conv2D)           (None, 28, 28, 512)       2359808   
                                                                 
 conv2d_9 (Conv2D)           (None, 28, 28, 512)       2359808   
                                                                 
 max_pooling2d_3 (MaxPooling  (None, 14, 14, 512)      0         
 2D)                                                             
                                                                 
 conv2d_10 (Conv2D)          (None, 14, 14, 512)       2359808   
                                                                 
 conv2d_11 (Conv2D)          (None, 14, 14, 512)       2359808   
                                                                 
 conv2d_12 (Conv2D)          (None, 14, 14, 512)       2359808   
                                                                 
 max_pooling2d_4 (MaxPooling  (None, 7, 7, 512)        0         
 2D)                                                             
                                                                 
 flatten (Flatten)           (None, 25088)             0         
                                                                 
 dense (Dense)               (None, 4096)              102764544 
                                                                 
 dense_1 (Dense)             (None, 4096)              16781312  
                                                                 
 dense_2 (Dense)             (None, 4)                 16388     
                                                                 
=================================================================
Total params: 134,276,932
Trainable params: 134,276,932
Non-trainable params: 0
_________________________________________________________________

4. 网络编译

在网络编译时.compile(),指定损失loss采用交叉熵损失,设置参数from_logits=True,由于网络的输出层没有使用softmax函数将输出的实数转为概率,参数设置为True时,会自动将logits的实数转为概率值,再和真实值计算损失,这里的真实值y是经过onehot编码之后的结果。

#(5)模型配置
# 设置优化器
opt = optimizers.Adam(learning_rate=1e-4)  # 学习率

model.compile(optimizer=opt, #学习率
              loss=keras.losses.CategoricalCrossentropy(from_logits=True), #损失
              metrics=['accuracy']) #评价指标

# 训练,给定训练集、验证集
history = model.fit(train_ds, validation_data=val_ds, epochs=30) #迭代30次


#(6)循环结束后绘制损失和准确率的曲线
# ==1== 准确率
train_acc = history.history['accuracy']  #训练集准确率
val_acc = history.history['val_accuracy']  #验证集准确率
# ==2== 损失
train_loss = history.history['loss'] #训练集损失
val_loss = history.history['val_loss'] #验证集损失
# ==3== 绘图
epochs_range = range(len(train_acc))
plt.figure(figsize=(10,5))
# 准确率
plt.subplot(1,2,1)
plt.plot(epochs_range, train_acc, label='train_acc')
plt.plot(epochs_range, val_acc, label='val_acc')
plt.legend()
# 损失曲线
plt.subplot(1,2,2)
plt.plot(epochs_range, train_loss, label='train_loss')
plt.plot(epochs_range, val_loss, label='val_loss')
plt.legend()

5. 结果展示

如图可见网络效果预测较好,在迭代至25次左右时网络准确率达到99%左右,如果迭代次数较多的话,可考虑在编译时使用early stopping保存最优权重,若后续网络效果都没有提升就可以提早停止网络,节约训练时间。

训练过程中的损失和准确率如下

Epoch 1/30
13/13 [==============================] - 7s 293ms/step - loss: 1.3627 - accuracy: 0.3116 - val_loss: 1.3483 - val_accuracy: 0.5075
Epoch 2/30
13/13 [==============================] - 3s 173ms/step - loss: 1.1267 - accuracy: 0.5251 - val_loss: 1.0235 - val_accuracy: 0.5226
------------------------------------------------------------------------------------------
省略N行
------------------------------------------------------------------------------------------
Epoch 26/30
13/13 [==============================] - 2s 174ms/step - loss: 0.1184 - accuracy: 0.9874 - val_loss: 0.1093 - val_accuracy: 0.9774
Epoch 27/30
13/13 [==============================] - 2s 174ms/step - loss: 0.3208 - accuracy: 0.9196 - val_loss: 0.2678 - val_accuracy: 0.9347
Epoch 28/30
13/13 [==============================] - 2s 172ms/step - loss: 0.2366 - accuracy: 0.9322 - val_loss: 0.1247 - val_accuracy: 0.9648
Epoch 29/30
13/13 [==============================] - 3s 173ms/step - loss: 0.1027 - accuracy: 0.9648 - val_loss: 0.0453 - val_accuracy: 0.9849
Epoch 30/30
13/13 [==============================] - 3s 171ms/step - loss: 0.0491 - accuracy: 0.9849 - val_loss: 0.0250 - val_accuracy: 0.9925

6. 其他方法

如果想更灵活的计算损失和准确率,可以不使用.compile(),.fit()函数。在模型构建完之后,自己敲一下代码实现前向传播,同样能实现模型训练效果。下面的代码可以代替第4小节中的第(5)步

# 指定优化器
optimizer = optimizers.Adam(learning_rate=1e-5)    
# 记录训练和测试过程中的每个batch的准确率和损失
train_acc = []  
train_loss = []
val_acc = []
val_loss = []

# 大循环
for epochs in range(30): #循环30次
    train_total_sum=0
    train_total_loss=0
    train_total_correct=0
    
    val_total_sum=0
    val_total_loss=0
    val_total_correct=0    
    
    #(5)网络训练
    for step, (x,y) in enumerate(train_ds): #每次从训练集中取出一个batch
        
        # 梯度跟踪
        with tf.GradientTape() as tape:
            # 前向传播
            logits = model(x)  # 输出属于每个分类的实数值
            
            # 计算准确率
            prob = tf.nn.softmax(logits, axis=1) # 计算概率
            predict = tf.argmax(prob, axis=1, output_type=tf.int32) # 概率最大值的下标
            correct = tf.cast(tf.equal(predict, y), dtype=tf.int32) # 对比预测值和真实值,将结果从布尔类型转变为1和0
            correct = tf.reduce_sum(correct) # 计算一共预测对了几个
            total = x.shape[0] # 每次迭代有多少参与进来
            train_total_sum += total #记录一共有多少个样本参与了循环
            train_total_correct += correct #记录一整次循环下来预测对了几个
            
            acc = correct/total # 每一个batch的准确率
            train_acc.append(acc) # 将每一个batch的准确率保存下来
            
            # 计算损失
            y = tf.one_hot(y, depth=4) # 对真实值进行onehot编码,分为4类
            loss = tf.losses.categorical_crossentropy(y, logits, from_logits=True) # 将预测值放入softmax种再计算损失
            # 求每个batch的损失均值
            loss_avg = tf.reduce_mean(loss, axis=1)
            
            # 记录总损失
            train_total_loss += tf.reduce_sum(loss)  #记录每个batch的损失          

        
        # 梯度计算,因变量为loss损失,自变量为模型中所有的权重和偏置
        grads = tape.gradient(loss_avg, model.trainable_variables)
        # 梯度更新,对所有的权重和偏置更新梯度
        optimizer.apply_gradients(zip(grads, model.trainable_variables))
    
        # 每20个batch打印一次损失和准确率
        if step%20 == 0:
            print('train', 'step:', step, 'loss:', loss_avg, 'acc:', acc)
    
    # 记录每次循环的损失和准确率
    train_acc.append(train_total_correct/train_total_sum) # 总预测对的个数除以总个数,平均准确率
    train_loss.append(train_total_loss/train_total_sum) # 总损失处于总个数,得平均损失
            
            
    #(6)网络测试
    for step, (x, y) in enumerate(val_ds):  #每次取出一个batch的验证数据
        # 前向传播
        logits = model(x)
        
        # 计算准确率
        prob = tf.nn.softmax(logits, axis=1) # 计算概率
        predict = tf.argmax(prob, axis=1, output_type=tf.int32) # 概率最大值的下标
        correct = tf.cast(tf.equal(predict, y), dtype=tf.int32) # 对比预测值和真实值,将结果从布尔类型转变为1和0
        correct = tf.reduce_sum(correct) # 计算一共预测对了几个
        val_total_correct += correct # 计算整个循环预测对了几个
        total = x.shape[0] # 每次迭代有多少参与进来
        val_total_sum += total # 整个循环有多少参与进来
        
        acc = correct/total # 每一个batch的准确率
        val_acc.append(acc) # 将每一个batch的准确率保存下来
            
        # 计算损失
        y = tf.one_hot(y, depth=4) # 对真实值进行onehot编码,分为4类
        loss = tf.losses.categorical_crossentropy(y, logits, from_logits=True) # 将预测值放入softmax种再计算损失
        # 求每个batch的损失均值
        loss_avg = tf.reduce_mean(loss, axis=1)
        
        # 记录总损失
        val_total_loss += tf.reduce_sum(loss) 

        # 每10个btch打印一次准确率和损失
        if step%10 == 0:
            print('val', 'step:', step, 'loss:', loss_avg, 'acc:', acc)
    
    # 记录每次循环的损失和准确率
    val_acc.append(val_total_correct/val_total_sum) # 总预测对的个数除以总个数,平均准确率
    val_loss.append(val_total_loss/val_total_sum) # 总损失处于总个数,得平均损失
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/dgvv4/article/details/121891454

智能推荐

12.使用hashcat暴力破解加密office文档密码_我是simeon的博客-程序员秘密

Word软件是目前世界上使用最为广泛的办公文字处理软件之一,在国内应该有超过90%的用户在使用它。政府、企业公司以及个人都喜欢用Word文件来处理工作和个人事务,而在使用Word文件来保存文件的内容时,根据不同的安全需要,需要对文件内容进行保护时就需要进行加密,而需要阅读文件内容时就需要解密。很多个人都有记日记的习惯,可时间久了,往往会忘记自己的密码,还有就是入侵过程中获取了一些加密文件,而此时又很想看看这些文件中的内容,使用一些word破解软件,往往对于密码设置复杂一点的就无能为力了。目前网上有很多关于

Python——画一棵漂亮的樱花树(不同种樱花+玫瑰+圣诞树喔_python樱花树代码排版_Nero_czh的博客-程序员秘密

原创 Python——画一棵漂亮的樱花树(不同种樱花+玫瑰+圣诞树喔) ...

python系列四 元组(tuple)_ACoderLife的博客-程序员秘密

1.元组定义:python中将不能修改的值称为不可变的,而不可变的列表被称为元组。2.定义一个元组:使用圆括号()来标识元组,中间用逗号(,)来分隔中间的元素。如:如果只定义1个元素的元组,必须如下定义:根据代码打印结果,不加逗号(,),定义的仅仅是一个变量。加了逗号(,),才是tuple。因为tuple是不可变的列表,所以对tuple进行insert,append等修改操作,会运行错误如apen...

Token和JWT的区别_jwt和token区别_风神修罗使的博客-程序员秘密

服务端验证客户端发送的token信息要进行数据的查询操作Jwt验证客户端发来的token就不用,在服务端使用密钥校验就可以了,不用数据库的查询。Token需要查库验证token 是否有效,而JWT不用查库或者少查库,直接在服务端进行校验,并且不用查库。因为用户的信息及加密信息在第二部分payload和第三部分签证中已经生成,只要在服务端进行校验就行,并且校验也是JWT自己实现的。TOKEN概念: 令牌, 是访问资源的凭证。Token的认证流程:用户输入用户名和密码,发送给服务器。服务器验证用户

Java 删除数组中重复的元素_无知的疯子的博客-程序员秘密

我是将数组封装为类的。 其中方法说明:getmax(){   //计算数据个数。getData(int index)得到index的元素。setData(int index,int b)设置索引为index的元素值为b.public void noDup(){//循环嵌套将重复的设为-1  int max = this.getmax();  for(int i = 0;i

手机App性能测试工具Genymotion安卓模拟器使用和简介_genymotion模拟器手机版_「QT(C++)开发工程师」的博客-程序员秘密

手机APP测试知识测试环境准备windosadb:参考《Applum自动化测试教程》2-3初识Android SDK【51zxw】python3:参考《Selenium自动化测试用例》3-2Python安装与配置【51zxw】移动设备Android(Genymotion模拟器)下载地址:https://www.genymotion.com/download/(推荐with VirtualBox)先注册地址:https://www.genymotion.com/account/create/

随便推点

央视国际节目定价发布接口规范C2_c2接口规范_it届的uzi的博客-程序员秘密

央视国际节目定价发布接口规范 V2.7.2                    央视国际IP电视事业部2011年3月Revision History Revision #AuthorReviewed ByA. Description Of ChangeB. Summary of ReviewIssued by / Date1.0  第1版2008-05-052.0  第2版Program和Ser...

SDUTOJ 3343 - 数据结构实验之二叉树四:(先序中序)还原二叉树_MokylinJay的博客-程序员秘密

Problem Description给定一棵二叉树的先序遍历序列和中序遍历序列,要求计算该二叉树的高度。Input输入数据有多组,每组数据第一行输入1个正整数N(1 <= N <= 50)为树中结点总数,随后2行先后给出先序和中序遍历序列,均是长度为N的不包含重复英文字母(区分大小写)的字符串。Output输出一个整数,即该二叉树的高度。Sample Input9AB...

pikachu-SQL注入_小白头发少的博客-程序员秘密

pikachu-SQL注入1.数字型(post)​ 使用burp抓包,并进行联合注入判断字段数爆出数据库名爆出表名爆出字段名爆出用户名和密码2.字符型注入(get)​ 同样使用联合注入但是需要闭合单引号# 搜索框内输入1' order by 3 #1' order by 2 #-1' union select 1,group_concat(schema_name) from information_schema.schemata #-1' union select

Mac安装Homebrew_homebrew core_ElenaYu的博客-程序员秘密

文章目录1、Homebrew是什么?2、设置环境变量3、更换为国内镜像源4、Homebrew基本用法1、Homebrew是什么?引用官方的一句话:Homebrew是Mac OS 不可或缺的套件管理器。Homebrew是一款Mac OS平台下的软件包管理工具,拥有安装、卸载、更新、查看、搜索等很多实用的功能。简单的一条指令,就可以实现包管理,而不用你关心各种依赖和文件路径的情况,十分方便快捷。ARM版Homebrew最终被安装在/opt/homebrew路径下。首先打开终端, 执行如下命令:/bi

Linux目录最顶端为,Linux目录结构(一)_子维酱的博客-程序员秘密

linux文件系统的最顶端是/,称为linux的root,所有的目录、文件、设备都在/之下。文件类型linux有四种基本文件系统类型:普通文件、目录文件、连续文件和特殊文件。可以用file命令来识别。普通文件:如文本文件、c语言源代码、shell脚本等,可以用cat、less、more、vi等来察看内容,用mv来改名。目录文件:包括文件名、子目录名及其指针。可以用ls列出目录文件链接文件:是指向一...

java.util.date java.sql.date java.sql.timestamp[轉]_hahahaha7551的博客-程序员秘密

java.util.date java.sql.date java.sql.timestamp[轉]http://hi.baidu.com/zhangyx/blog/item/5fdf0a4f1b9f573caec3ab52.html 整理一:这里的一片文章,我个人认为讲解的很详细,有对 java.sql.Date的使用还有困惑的请看。java.sql.Date 只存储日期数据不存

推荐文章

热门文章

相关标签